diff --git a/stuff/config/qss/Dark/Dark.qss b/stuff/config/qss/Dark/Dark.qss index b6a98c10..4a76256d 100644 --- a/stuff/config/qss/Dark/Dark.qss +++ b/stuff/config/qss/Dark/Dark.qss @@ -1706,6 +1706,15 @@ ProjectPopup QLabel { padding-left: 5px; padding-right: 5px; } +/* ----------------------------------------------------------------------------- + Frame format settings box in FormatSettingsPopup +----------------------------------------------------------------------------- */ +#FrameFormatBoxInput { + border-color: rgba(124, 10, 10, 0.82); +} +#FrameFormatBoxOutput { + border-color: rgba(10, 10, 124, 0.82); +} /* ----------------------------------------------------------------------------- Schematic Viewer ----------------------------------------------------------------------------- */ diff --git a/stuff/config/qss/Darker/Darker.qss b/stuff/config/qss/Darker/Darker.qss index f7472024..36d85ab2 100644 --- a/stuff/config/qss/Darker/Darker.qss +++ b/stuff/config/qss/Darker/Darker.qss @@ -1706,6 +1706,15 @@ ProjectPopup QLabel { padding-left: 5px; padding-right: 5px; } +/* ----------------------------------------------------------------------------- + Frame format settings box in FormatSettingsPopup +----------------------------------------------------------------------------- */ +#FrameFormatBoxInput { + border-color: rgba(118, 4, 4, 0.82); +} +#FrameFormatBoxOutput { + border-color: rgba(4, 4, 118, 0.82); +} /* ----------------------------------------------------------------------------- Schematic Viewer ----------------------------------------------------------------------------- */ diff --git a/stuff/config/qss/Light/Light.qss b/stuff/config/qss/Light/Light.qss index 97d8766f..adef9c2c 100644 --- a/stuff/config/qss/Light/Light.qss +++ b/stuff/config/qss/Light/Light.qss @@ -1706,6 +1706,15 @@ ProjectPopup QLabel { padding-left: 5px; padding-right: 5px; } +/* ----------------------------------------------------------------------------- + Frame format settings box in FormatSettingsPopup +----------------------------------------------------------------------------- */ +#FrameFormatBoxInput { + border-color: rgba(207, 93, 93, 0.82); +} +#FrameFormatBoxOutput { + border-color: rgba(93, 93, 207, 0.82); +} /* ----------------------------------------------------------------------------- Schematic Viewer ----------------------------------------------------------------------------- */ diff --git a/stuff/config/qss/Medium/Medium.qss b/stuff/config/qss/Medium/Medium.qss index 81da8d77..8173787e 100644 --- a/stuff/config/qss/Medium/Medium.qss +++ b/stuff/config/qss/Medium/Medium.qss @@ -1706,6 +1706,15 @@ ProjectPopup QLabel { padding-left: 5px; padding-right: 5px; } +/* ----------------------------------------------------------------------------- + Frame format settings box in FormatSettingsPopup +----------------------------------------------------------------------------- */ +#FrameFormatBoxInput { + border-color: rgba(138, 24, 24, 0.82); +} +#FrameFormatBoxOutput { + border-color: rgba(24, 24, 138, 0.82); +} /* ----------------------------------------------------------------------------- Schematic Viewer ----------------------------------------------------------------------------- */ diff --git a/stuff/config/qss/Medium/less/layouts/popups.less b/stuff/config/qss/Medium/less/layouts/popups.less index b2054249..f675257c 100644 --- a/stuff/config/qss/Medium/less/layouts/popups.less +++ b/stuff/config/qss/Medium/less/layouts/popups.less @@ -118,3 +118,13 @@ ProjectPopup { padding-left: 5px; padding-right: 5px; } + +/* ----------------------------------------------------------------------------- + Frame format settings box in FormatSettingsPopup +----------------------------------------------------------------------------- */ +#FrameFormatBoxInput { + border-color: mix(@accent, rgba(255, 0, 0, 0.7), 40); +} +#FrameFormatBoxOutput { + border-color: mix(@accent, rgba(0, 0, 255, 0.7), 40); +} diff --git a/stuff/config/qss/Neutral/Neutral.qss b/stuff/config/qss/Neutral/Neutral.qss index f4dbe5cd..f6ccac09 100644 --- a/stuff/config/qss/Neutral/Neutral.qss +++ b/stuff/config/qss/Neutral/Neutral.qss @@ -1706,6 +1706,15 @@ ProjectPopup QLabel { padding-left: 5px; padding-right: 5px; } +/* ----------------------------------------------------------------------------- + Frame format settings box in FormatSettingsPopup +----------------------------------------------------------------------------- */ +#FrameFormatBoxInput { + border-color: rgba(164, 50, 50, 0.82); +} +#FrameFormatBoxOutput { + border-color: rgba(50, 50, 164, 0.82); +} /* ----------------------------------------------------------------------------- Schematic Viewer ----------------------------------------------------------------------------- */ diff --git a/toonz/sources/common/timage_io/tlevel_io.cpp b/toonz/sources/common/timage_io/tlevel_io.cpp index 4451d5e7..3a6e379f 100644 --- a/toonz/sources/common/timage_io/tlevel_io.cpp +++ b/toonz/sources/common/timage_io/tlevel_io.cpp @@ -186,7 +186,8 @@ TLevelWriter::TLevelWriter(const TFilePath &path, TPropertyGroup *prop) : TSmartObject(m_classCode) , m_path(path) , m_properties(prop) - , m_contentHistory(0) { + , m_contentHistory(0) + , m_frameFormatTemplateFId(TFrameId::NO_FRAME) { string ext = path.getType(); if (!prop) m_properties = Tiio::makeWriterProperties(ext); } @@ -250,6 +251,12 @@ void TLevelWriter::getSupportedFormats(QStringList &names, //----------------------------------------------------------- TImageWriterP TLevelWriter::getFrameWriter(TFrameId fid) { + // change the frame format with the template + if (!m_frameFormatTemplateFId.isNoFrame()) { + fid.setZeroPadding(m_frameFormatTemplateFId.getZeroPadding()); + fid.setStartSeqInd(m_frameFormatTemplateFId.getStartSeqInd()); + } + TImageWriterP iw(m_path.withFrame(fid)); iw->setProperties(m_properties); return iw; diff --git a/toonz/sources/common/tsystem/tfilepath.cpp b/toonz/sources/common/tsystem/tfilepath.cpp index 4877459a..0f28bb55 100644 --- a/toonz/sources/common/tsystem/tfilepath.cpp +++ b/toonz/sources/common/tsystem/tfilepath.cpp @@ -573,6 +573,36 @@ std::string TFilePath::getDots() const { //----------------------------------------------------------------------------- +QChar TFilePath::getSepChar() const { + if (!TFilePath::m_useStandard) return analyzePath().sepChar; + //----- + QString type = QString::fromStdString(getType()).toLower(); + if (isFfmpegType()) return QChar(); + int i = getLastSlash(m_path); + std::wstring str = m_path.substr(i + 1); + // potrei anche avere a.b.c.d dove d e' l'estensione + i = str.rfind(L"."); + if (i == (int)std::wstring::npos || str == L"..") return QChar(); + + int j = str.substr(0, i).rfind(L"."); + + if (j != (int)std::wstring::npos) + return (j == i - 1 || (checkForSeqNum(type) && isNumbers(str, j, i))) + ? QChar('.') + : QChar(); + if (!m_underscoreFormatAllowed) return QChar(); + + j = str.substr(0, i).rfind(L"_"); + if (j != (int)std::wstring::npos) + return (j == i - 1 || (checkForSeqNum(type) && isNumbers(str, j, i))) + ? QChar('_') + : QChar(); + else + return QChar(); +} + +//----------------------------------------------------------------------------- + std::string TFilePath::getDottedType() const // ritorna l'estensione con PUNTO (se c'e') { @@ -881,7 +911,8 @@ TFilePath TFilePath::withFrame(const TFrameId &frame, if (frame.isNoFrame()) { return TFilePath(info.parentDir + info.levelName + "." + info.extension); } - QString sepChar = (info.sepChar.isNull()) ? "." : QString(info.sepChar); + QString sepChar = (info.sepChar.isNull()) ? QString(frame.getStartSeqInd()) + : QString(info.sepChar); return TFilePath(info.parentDir + info.levelName + sepChar + QString::fromStdString(frame.expand(format)) + "." + diff --git a/toonz/sources/common/tvrender/tfont_qt.cpp b/toonz/sources/common/tvrender/tfont_qt.cpp index ce5417d3..7d1cd759 100644 --- a/toonz/sources/common/tvrender/tfont_qt.cpp +++ b/toonz/sources/common/tvrender/tfont_qt.cpp @@ -176,15 +176,20 @@ TPoint TFont::drawChar(QImage &outImage, TPoint &unused, wchar_t charcode, // alphaMapForGlyph with a space character returns an invalid // QImage for some reason. // Bug 3604: https://github.com/opentoonz/opentoonz/issues/3604 -#ifdef Q_OS_UNIX - if (chars[0] == L' ') { - outImage = QImage(raw.averageCharWidth(), raw.ascent() + raw.descent(), - QImage::Format_Grayscale8); - outImage.fill(255); - return getDistance(charcode, nextCharCode); - } + // (21/1/2022) Use this workaround for all platforms as the crash also occured + // in windows when the display is scaled up. + if (chars[0].isSpace()) { +#if QT_VERSION >= 0x051100 + int w = QFontMetrics(m_pimpl->m_font).horizontalAdvance(chars[0]); +#else + int w = raw.averageCharWidth(); #endif + outImage = + QImage(w, raw.ascent() + raw.descent(), QImage::Format_Grayscale8); + outImage.fill(255); + return getDistance(charcode, nextCharCode); + } QImage image = raw.alphaMapForGlyph(indices[0], QRawFont::PixelAntialiasing); if (image.format() != QImage::Format_Indexed8 && image.format() != QImage::Format_Alpha8) diff --git a/toonz/sources/include/tfilepath.h b/toonz/sources/include/tfilepath.h index b7ff3609..301dfde4 100644 --- a/toonz/sources/include/tfilepath.h +++ b/toonz/sources/include/tfilepath.h @@ -316,6 +316,8 @@ type is a string that indicate the filename extension(ex:. bmp or .bmp)*/ void split(std::wstring &head, TFilePath &tail) const; TFilePathInfo analyzePath() const; + + QChar getSepChar() const; }; //----------------------------------------------------------------------------- diff --git a/toonz/sources/include/tlevel_io.h b/toonz/sources/include/tlevel_io.h index 71878ccf..f5bd0ea7 100644 --- a/toonz/sources/include/tlevel_io.h +++ b/toonz/sources/include/tlevel_io.h @@ -82,12 +82,11 @@ public: static FormatType getFormatType(std::string extension); - static void define( - QString extension, int reader, - // nel caso in cui ci siano piu' lettori per lo stesso formato - // (es. flash) + static void define(QString extension, int reader, + // nel caso in cui ci siano piu' lettori per lo stesso + // formato (es. flash) - TLevelReaderCreateProc *proc); + TLevelReaderCreateProc *proc); static inline void define(QString extension, TLevelReaderCreateProc *proc) { define(extension, 0, proc); @@ -136,6 +135,9 @@ protected: TContentHistory *m_contentHistory; QString m_creator; + // if template is not used, frame number is set to TFrameId::NO_FRAME + TFrameId m_frameFormatTemplateFId; + public: TLevelWriter(const TFilePath &path, TPropertyGroup *winfo = 0); // ottiene l'ownership @@ -203,6 +205,10 @@ public: static void define(QString extension, TLevelWriterCreateProc *proc, bool isRenderFormat); + + void setFrameFormatTemplateFId(const TFrameId &tmplFId) { + m_frameFormatTemplateFId = tmplFId; + } }; //----------------------------------------------------------- diff --git a/toonz/sources/include/toonz/levelupdater.h b/toonz/sources/include/toonz/levelupdater.h index 5666b9c3..f8657f73 100644 --- a/toonz/sources/include/toonz/levelupdater.h +++ b/toonz/sources/include/toonz/levelupdater.h @@ -124,7 +124,8 @@ class DVAPI LevelUpdater { public: LevelUpdater(); LevelUpdater(TXshSimpleLevel *sl); - LevelUpdater(const TFilePath &path, TPropertyGroup *lwProperties = 0); + LevelUpdater(const TFilePath &path, TPropertyGroup *lwProperties = 0, + const TFrameId &tmplFId = TFrameId(TFrameId::NO_FRAME)); ~LevelUpdater(); TLevelWriterP getLevelWriter() { return m_lw; } @@ -142,7 +143,8 @@ public: //! This function may throw in case the specified path has an unrecognized //! extension, or the file could //! not be opened for write. - void open(const TFilePath &src, TPropertyGroup *lwProperties); + void open(const TFilePath &src, TPropertyGroup *lwProperties, + const TFrameId &tmplFId); //! Attaches the updater to the specified simple level instance. Format //! properties are the default in case the level diff --git a/toonz/sources/include/toonz/preferences.h b/toonz/sources/include/toonz/preferences.h index 00c2dd98..17911ea5 100644 --- a/toonz/sources/include/toonz/preferences.h +++ b/toonz/sources/include/toonz/preferences.h @@ -9,6 +9,7 @@ #include "tcommon.h" #include "tgeometry.h" #include "tpixel.h" +#include "tfilepath.h" // TnzLib includes #include "toonz/levelproperties.h" @@ -36,7 +37,6 @@ // Forward declarations -class TFilePath; class QSettings; //============================================================== @@ -295,7 +295,8 @@ public: int getRhubarbTimeout() { return getIntValue(rhubarbTimeout); } // Drawing tab - QString getScanLevelType() const { return getStringValue(scanLevelType); } + QString getDefRasterFormat() const { return getStringValue(DefRasterFormat); } + // QString getScanLevelType() const { return getStringValue(scanLevelType); } int getDefLevelType() const { return getIntValue(DefLevelType); } bool isNewLevelSizeToCameraSizeEnabled() const { return getBoolValue(newLevelSizeToCameraSizeEnabled); diff --git a/toonz/sources/include/toonz/preferencesitemids.h b/toonz/sources/include/toonz/preferencesitemids.h index 7ecf5069..8a4e44de 100644 --- a/toonz/sources/include/toonz/preferencesitemids.h +++ b/toonz/sources/include/toonz/preferencesitemids.h @@ -82,7 +82,8 @@ enum PreferencesItemId { //---------- // Drawing - scanLevelType, + DefRasterFormat, + // scanLevelType,// deprecated DefLevelType, newLevelSizeToCameraSizeEnabled, DefLevelWidth, diff --git a/toonz/sources/include/toonz/sceneproperties.h b/toonz/sources/include/toonz/sceneproperties.h index 2ac27e1f..c8ff7922 100644 --- a/toonz/sources/include/toonz/sceneproperties.h +++ b/toonz/sources/include/toonz/sceneproperties.h @@ -288,6 +288,10 @@ and height. bool hasDefaultCellMarks() const; // check if the cell mark settings are modified + // templateFId in preview settings is used for "input" file format + // such as new raster level, captured images by camera capture feature, etc. + TFrameId &formatTemplateFIdForInput(); + private: // not implemented TSceneProperties(const TSceneProperties &); diff --git a/toonz/sources/include/toonz/txshsimplelevel.h b/toonz/sources/include/toonz/txshsimplelevel.h index 0a2c2b9f..2ed4b8e4 100644 --- a/toonz/sources/include/toonz/txshsimplelevel.h +++ b/toonz/sources/include/toonz/txshsimplelevel.h @@ -185,6 +185,7 @@ table) it returns the proper insertion index //! from each entry to the next. int guessStep() const; + void formatFId(TFrameId &fid, TFrameId tmplFId); void setFrame(const TFrameId &fid, const TImageP &img); TImageP getFrame(const TFrameId &fid, UCHAR imgManagerParamsMask, diff --git a/toonz/sources/include/toonzqt/imageutils.h b/toonz/sources/include/toonzqt/imageutils.h index ce355d90..95d3ba1a 100644 --- a/toonz/sources/include/toonzqt/imageutils.h +++ b/toonz/sources/include/toonzqt/imageutils.h @@ -98,10 +98,11 @@ void DVAPI convert( const TPixel &bgColor = TPixel::Transparent, //!< Destination Background color. bool removeDotBeforeFrameNumber = - false /*-- ConvertPopup - での指定に合わせて、[レベル名].[フレーム番号].[拡張子]のうち、 - [レベル名]と[フレーム番号]の間のドットを消す。 --*/ -); //!< Converts a saved level to fullcolor, and saves the result. + false, /*-- ConvertPopup + での指定に合わせて、[レベル名].[フレーム番号].[拡張子]のうち、 + [レベル名]と[フレーム番号]の間のドットを消す。 --*/ + const TFrameId &tmplFId = TFrameId() //!< frame format template +); //!< Converts a saved level to fullcolor, and saves the result. void DVAPI convertNaa2Tlv( const TFilePath &source, //!< Level path to convert from. diff --git a/toonz/sources/include/toutputproperties.h b/toonz/sources/include/toutputproperties.h index f22c0269..5647a113 100644 --- a/toonz/sources/include/toutputproperties.h +++ b/toonz/sources/include/toutputproperties.h @@ -94,6 +94,10 @@ private: BoardSettings *m_boardSettings; + // templateFId in preview settings is used for "input" file format + // such as new raster level, captured images by camera capture feature, etc. + TFrameId m_formatTemplateFId; + public: /*! Constructs TOutputProperties with default value. @@ -221,6 +225,8 @@ machine's CPU). void setSubcameraPreview(bool enabled) { m_subcameraPreview = enabled; } BoardSettings *getBoardSettings() const { return m_boardSettings; } + + TFrameId &formatTemplateFId() { return m_formatTemplateFId; } }; //-------------------------------------------- diff --git a/toonz/sources/stdfx/iwa_fractalnoisefx.cpp b/toonz/sources/stdfx/iwa_fractalnoisefx.cpp index c694f241..486170c2 100644 --- a/toonz/sources/stdfx/iwa_fractalnoisefx.cpp +++ b/toonz/sources/stdfx/iwa_fractalnoisefx.cpp @@ -33,7 +33,7 @@ inline const T &clamp(const T &v, const T &lo, const T &hi) { const double turbulentGamma = 2.2; // magic number to offset evolution between generations -const double evolutionOffsetStep = 19.82; +const double evolutionOffsetStep = 19.82; const double evolutionOffsetStepW = 31.1; } // namespace //------------------------------------------------------------------ @@ -61,11 +61,11 @@ Iwa_FractalNoiseFx::Iwa_FractalNoiseFx() ///, m_randomSeed(0) , m_dynamicIntensity(1.0) , m_alphaRendering(false) -, m_doConical(false) -, m_conicalEvolution(0.0) -, m_conicalAngle(60.0) -, m_cameraFov(60.0) -, m_zScale(2.0) { + , m_doConical(false) + , m_conicalEvolution(0.0) + , m_conicalAngle(60.0) + , m_cameraFov(60.0) + , m_zScale(2.0) { m_fractalType->addItem(TurbulentSmooth, "Turbulent Smooth"); m_fractalType->addItem(TurbulentBasic, "Turbulent Basic"); m_fractalType->addItem(TurbulentSharp, "Turbulent Sharp"); @@ -167,7 +167,7 @@ void Iwa_FractalNoiseFx::doCompute(TTile &tile, double frame, double *work_buf = (double *)work_buf_ras->getRawData(); // affine transformations - TAffine globalAff = TTranslation(-tile.m_pos) * ri.m_affine; + TAffine globalAff = TTranslation(-tile.m_pos) * ri.m_affine; TAffine parentAff = TScale(param.scale.lx, param.scale.ly) * TRotation(-param.rotation); TAffine subAff = TTranslation(param.subOffset) * TScale(param.subScaling) * @@ -195,34 +195,34 @@ void Iwa_FractalNoiseFx::doCompute(TTile &tile, double frame, for (int gen = 0; gen < genCount; gen++) { // affine transformation for the current generation TAffine currentAff = - (globalAff * parentOffsetAff * parentAff * genAff).inv(); + (globalAff * parentOffsetAff * parentAff * genAff).inv(); // scale of the current pattern ( used for the Dynamic / Dynamic Twist // offset ) double scale = sqrt(std::abs(currentAff.det())); // for each pixel - double* buf_p = work_buf; + double *buf_p = work_buf; for (int y = 0; y < outDim.ly; y++) { for (int x = 0; x < outDim.lx; x++, buf_p++) { // obtain sampling position // For Dynamic and Dynamic Twist patterns, the position offsets using // gradient / rotation of the parent pattern TPointD samplePos = - getSamplePos(x, y, outDim, out_buf, gen, scale, param); + getSamplePos(x, y, outDim, out_buf, gen, scale, param); // multiply affine transformation samplePos = currentAff * samplePos; // adjust position for the block pattern if (param.noiseType == Block) samplePos = TPointD(std::floor(samplePos.x) + 0.5, - std::floor(samplePos.y) + 0.5); + std::floor(samplePos.y) + 0.5); // calculate the base noise if (param.cycleEvolution) *buf_p = (pn.noise(samplePos.x, samplePos.y, evolution_zw.x, - evolution_zw.y) + - 1.0) * - 0.5; + evolution_zw.y) + + 1.0) * + 0.5; else *buf_p = - (pn.noise(samplePos.x, samplePos.y, evolution_z) + 1.0) * 0.5; + (pn.noise(samplePos.x, samplePos.y, evolution_z) + 1.0) * 0.5; // convert the noise convert(buf_p, param); @@ -232,17 +232,16 @@ void Iwa_FractalNoiseFx::doCompute(TTile &tile, double frame, // just copy the values for the first generation if (gen == 0) { memcpy(out_buf, work_buf, outDim.lx * outDim.ly * sizeof(double)); - } - else { + } else { // intensity of the last generation will take the fraction part of // complexity double genIntensity = std::min(1.0, param.complexity - (double)gen); // influence of the current generation double influence = - genIntensity * std::pow(param.subInfluence, (double)gen); + genIntensity * std::pow(param.subInfluence, (double)gen); // composite the base noise pattern - buf_p = work_buf; - double* out_p = out_buf; + buf_p = work_buf; + double *out_p = out_buf; for (int i = 0; i < outDim.lx * outDim.ly; i++, buf_p++, out_p++) composite(out_p, buf_p, influence, param); } @@ -253,9 +252,9 @@ void Iwa_FractalNoiseFx::doCompute(TTile &tile, double frame, // according to the sub scale if (param.perspectiveOffset) parentOffsetAff = TScale(param.subScaling) * - TRotation(-param.subRotation) * parentOffsetAff * - TRotation(param.subRotation) * - TScale(1 / param.subScaling); + TRotation(-param.subRotation) * parentOffsetAff * + TRotation(param.subRotation) * + TScale(1 / param.subScaling); if (param.cycleEvolution) evolution_zw.x += evolutionOffsetStep; @@ -269,8 +268,8 @@ void Iwa_FractalNoiseFx::doCompute(TTile &tile, double frame, // angle of slope of the cone double theta_n = param.conicalAngle * M_PI_180; // half of the vertical fov - double phi_2 = param.cameraFov * 0.5 * M_PI_180; - double z_scale = std::pow(10.0, param.zScale); + double phi_2 = param.cameraFov * 0.5 * M_PI_180; + double z_scale = std::pow(10.0, param.zScale); double evolution_w = param.conicalEvolution; // pixel distance between camera and the screen @@ -284,83 +283,99 @@ void Iwa_FractalNoiseFx::doCompute(TTile &tile, double frame, // accumulate base noise pattern for each generation for (int gen = 0; gen < genCount; gen++) { // affine transformation for the current generation - TAffine currentAff = - (globalAff * parentAff * genAff).inv(); + TAffine currentAff = (globalAff * parentAff * genAff).inv(); // scale of the current pattern ( used for the Dynamic / Dynamic Twist // offset ) double scale = sqrt(std::abs(currentAff.det())); // for each pixel - double* buf_p = work_buf; + double *buf_p = work_buf; for (int y = 0; y < outDim.ly; y++) { for (int x = 0; x < outDim.lx; x++, buf_p++) { - double dx, dy, dz; if (theta_n == 0.0) { dx = x; dy = y; dz = 0.0; - } - else { + } else { // conical, without offset if (center == TPointD()) { TPointD p = TTranslation(tile.m_pos) * TPointD(x, y); // pixel distance from the screen center double d = tdistance(p, TPointD()); // line of sight : d = S * z + T - double S = d / D; - double T = d; - dz = (V - T) / (S - U); + double S = d / D; + double T = d; + dz = (V - T) / (S - U); double dp = S * dz + T; if (d != 0.0) { p.x *= dp / d; p.y *= dp / d; } p += center * (dp / V); - p = TTranslation(tile.m_pos).inv() * p; + p = TTranslation(tile.m_pos).inv() * p; dx = p.x; dy = p.y; dz /= z_scale; } // conical, with offset else { - // compute the intersecting point between the "noise cone" and the line of sight + // compute the intersecting point between the "noise cone" and the + // line of sight TPointD _p = TTranslation(tile.m_pos) * TPointD(x, y); - QVector3D p(_p.x, _p.y, 0.0); - QVector3D eye_O(center.x, center.y, -D); - QVector3D eye_d = (p - eye_O).normalized(); + // offset by combination of offsets of A) projection position and + // B) eye positon. + // + // A) 0.5 * projection position offset + QVector3D p(_p.x - center.x * 0.5, _p.y - center.y * 0.5, 0.0); QVector3D cone_C(0.0, 0.0, V * std::tan(theta_n)); QVector3D cone_a(0, 0, -1); + // B) 0.5 * eye position offset + double offsetAdj = 0.5 * (D + cone_C.z()) / cone_C.z(); + QVector3D eye_O(center.x * offsetAdj, center.y * offsetAdj, -D); + QVector3D eye_d = (p - eye_O).normalized(); double cos_ConeT = std::sin(theta_n); - float d_a = QVector3D::dotProduct(eye_d, cone_a); - float Ca_Oa = QVector3D::dotProduct(cone_C, cone_a) - QVector3D::dotProduct(eye_O, cone_a); + float d_a = QVector3D::dotProduct(eye_d, cone_a); + float Ca_Oa = QVector3D::dotProduct(cone_C, cone_a) - + QVector3D::dotProduct(eye_O, cone_a); // A * t^2 + B * t + C = 0 - float A = d_a * d_a - eye_d.lengthSquared() * cos_ConeT; - float B = 2.0 * (QVector3D::dotProduct(eye_d, cone_C) - QVector3D::dotProduct(eye_d, eye_O)) * cos_ConeT * cos_ConeT - - 2.0 * Ca_Oa * d_a; - float C = Ca_Oa * Ca_Oa - - cos_ConeT * cos_ConeT * (cone_C.lengthSquared() - 2.0 * QVector3D::dotProduct(eye_O, cone_C) + eye_O.lengthSquared()); + float A = + d_a * d_a - eye_d.lengthSquared() * cos_ConeT * cos_ConeT; + float B = 2.0 * + (QVector3D::dotProduct(eye_d, cone_C) - + QVector3D::dotProduct(eye_d, eye_O)) * + cos_ConeT * cos_ConeT - + 2.0 * Ca_Oa * d_a; + float C = Ca_Oa * Ca_Oa - + cos_ConeT * cos_ConeT * + (cone_C.lengthSquared() - + 2.0 * QVector3D::dotProduct(eye_O, cone_C) + + eye_O.lengthSquared()); // obtain t double t1 = (-B + std::sqrt(B * B - 4.0 * A * C)) / (2.0 * A); double t2 = (-B - std::sqrt(B * B - 4.0 * A * C)) / (2.0 * A); - if (t1 < 0) t1 = t2; - else if (t2 < 0) t2 = t1; + if (t1 < 0) + t1 = t2; + else if (t2 < 0) + t2 = t1; double t = std::min(t1, t2); // intersecting point QVector3D sampleP = eye_O + eye_d * t; - _p.x = sampleP.x(); - _p.y = sampleP.y(); - _p = TTranslation(tile.m_pos).inv() * _p; - dx = _p.x; - dy = _p.y; - dz = sampleP.z() / z_scale; + _p.x = sampleP.x(); + _p.y = sampleP.y(); + _p = TTranslation(tile.m_pos).inv() * _p; + dx = _p.x; + dy = _p.y; + dz = sampleP.z() / z_scale; } if (param.cycleEvolution) { - double cycle_theta = 2.0 * M_PI * (param.evolution + param.conicalEvolution + dz) / param.cycleEvolutionRange; + double cycle_theta = + 2.0 * M_PI * (param.evolution + param.conicalEvolution + dz) / + param.cycleEvolutionRange; double cycle_d = param.cycleEvolutionRange / (2.0 * M_PI); evolution_zw.x = cycle_d * cos(cycle_theta); evolution_zw.y = cycle_d * sin(cycle_theta); @@ -369,23 +384,28 @@ void Iwa_FractalNoiseFx::doCompute(TTile &tile, double frame, // obtain sampling position // For Dynamic and Dynamic Twist patterns, the position offsets using // gradient / rotation of the parent pattern + TPointD samplePosOffset = + getSamplePos(x, y, outDim, out_buf, gen, scale, param) - + TPointD(x, y); TPointD samplePos = - getSamplePos(dx, dy, outDim, out_buf, gen, scale, param); + TPointD(dx, dy) + samplePosOffset * (D / (D + dz)); // multiply affine transformation samplePos = currentAff * samplePos; // adjust position for the block pattern if (param.noiseType == Block) samplePos = TPointD(std::floor(samplePos.x) + 0.5, - std::floor(samplePos.y) + 0.5); + std::floor(samplePos.y) + 0.5); // calculate the base noise if (param.cycleEvolution) *buf_p = (pn.noise(samplePos.x, samplePos.y, evolution_zw.x, - evolution_zw.y) + - 1.0) * - 0.5; + evolution_zw.y) + + 1.0) * + 0.5; else - *buf_p = - (pn.noise(samplePos.x, samplePos.y, evolution_z, evolution_w + dz) + 1.0) * 0.5; + *buf_p = (pn.noise(samplePos.x, samplePos.y, evolution_z, + evolution_w + dz) + + 1.0) * + 0.5; // convert the noise convert(buf_p, param); @@ -395,17 +415,16 @@ void Iwa_FractalNoiseFx::doCompute(TTile &tile, double frame, // just copy the values for the first generation if (gen == 0) { memcpy(out_buf, work_buf, outDim.lx * outDim.ly * sizeof(double)); - } - else { + } else { // intensity of the last generation will take the fraction part of // complexity double genIntensity = std::min(1.0, param.complexity - (double)gen); // influence of the current generation double influence = - genIntensity * std::pow(param.subInfluence, (double)gen); + genIntensity * std::pow(param.subInfluence, (double)gen); // composite the base noise pattern - buf_p = work_buf; - double* out_p = out_buf; + buf_p = work_buf; + double *out_p = out_buf; for (int i = 0; i < outDim.lx * outDim.ly; i++, buf_p++, out_p++) composite(out_p, buf_p, influence, param); } @@ -422,7 +441,6 @@ void Iwa_FractalNoiseFx::doCompute(TTile &tile, double frame, } } - work_buf_ras->unlock(); // finalize pattern (converting the color space) @@ -483,13 +501,13 @@ void Iwa_FractalNoiseFx::obtainParams(FNParam ¶m, const double frame, param.cycleEvolutionRange = m_cycleEvolutionRange->getValue(frame); param.dynamicIntensity = m_dynamicIntensity->getValue(frame) * 10.0; - param.doConical = m_doConical->getValue(); + param.doConical = m_doConical->getValue(); param.conicalEvolution = m_conicalEvolution->getValue(frame); - param.conicalAngle = m_conicalAngle->getValue(frame); - param.cameraFov = m_cameraFov->getValue(frame); - param.zScale = m_zScale->getValue(frame); + param.conicalAngle = m_conicalAngle->getValue(frame); + param.cameraFov = m_cameraFov->getValue(frame); + param.zScale = m_zScale->getValue(frame); - param.alphaRendering = m_alphaRendering->getValue(); + param.alphaRendering = m_alphaRendering->getValue(); } //------------------------------------------------------------------ @@ -529,7 +547,8 @@ void Iwa_FractalNoiseFx::getParamUIs(TParamUIConcept *&concepts, int &length) { //------------------------------------------------------------------ // For Dynamic and Dynamic Twist patterns, the position offsets using gradient / // rotation of the parent pattern -TPointD Iwa_FractalNoiseFx::getSamplePos(double x, double y, const TDimension outDim, +TPointD Iwa_FractalNoiseFx::getSamplePos(double x, double y, + const TDimension outDim, const double *out_buf, const int gen, const double scale, const FNParam ¶m) { @@ -541,43 +560,47 @@ TPointD Iwa_FractalNoiseFx::getSamplePos(double x, double y, const TDimension ou auto clampPos = [&](double x, double y) { if (x < 0.0) x = 0.0; - else if (x > (double)(outDim.lx-1)) + else if (x > (double)(outDim.lx - 1)) x = (double)(outDim.lx - 1); if (y < 0.0) y = 0.0; - else if (y > (double)(outDim.ly-1)) + else if (y > (double)(outDim.ly - 1)) y = (double)(outDim.ly - 1); return TPointD(x, y); }; - auto val = [&](const TPoint& p) { - return out_buf[std::min(p.y, outDim.ly - 1) * outDim.lx + std::min(p.x, outDim.lx - 1)]; + auto val = [&](const TPoint &p) { + return out_buf[std::min(p.y, outDim.ly - 1) * outDim.lx + + std::min(p.x, outDim.lx - 1)]; }; auto lerp = [](double v0, double v1, double ratio) { return (1.0 - ratio) * v0 + ratio * v1; }; - auto lerpVal = [&](const TPointD &p) { - int id_x = (int)std::floor(p.x); + auto lerpVal = [&](const TPointD &p) { + int id_x = (int)std::floor(p.x); double ratio_x = p.x - (double)id_x; - int id_y = (int)std::floor(p.y); + int id_y = (int)std::floor(p.y); double ratio_y = p.y - (double)id_y; - return lerp ( lerp(val(TPoint(id_x, id_y)), val(TPoint(id_x+1, id_y)), ratio_x), - lerp(val(TPoint(id_x, id_y+1)), val(TPoint(id_x + 1, id_y+1)), ratio_x), ratio_y); + return lerp( + lerp(val(TPoint(id_x, id_y)), val(TPoint(id_x + 1, id_y)), ratio_x), + lerp(val(TPoint(id_x, id_y + 1)), val(TPoint(id_x + 1, id_y + 1)), + ratio_x), + ratio_y); }; - int range = std::max(2, (int)(0.1 / scale)); - TPointD left = clampPos(x - range, y); + int range = std::max(2, (int)(0.1 / scale)); + TPointD left = clampPos(x - range, y); TPointD right = clampPos(x + range, y); TPointD down = clampPos(x, y - range); TPointD up = clampPos(x, y + range); double dif_x = param.dynamicIntensity * (1 / scale) * (lerpVal(left) - lerpVal(right)) / (left.x - right.x); - double dif_y = param.dynamicIntensity * (1 / scale) * (lerpVal(up) - lerpVal(down)) / - (up.y - down.y); + double dif_y = param.dynamicIntensity * (1 / scale) * + (lerpVal(up) - lerpVal(down)) / (up.y - down.y); if (param.fractalType == Dynamic) return TPointD(x + dif_x, y + dif_y); // gradient - else // Dynamic_twist + else // Dynamic_twist return TPointD(x + dif_y, y - dif_x); // rotation } diff --git a/toonz/sources/stdfx/iwa_fractalnoisefx.h b/toonz/sources/stdfx/iwa_fractalnoisefx.h index e58d61b4..0debcbc2 100644 --- a/toonz/sources/stdfx/iwa_fractalnoisefx.h +++ b/toonz/sources/stdfx/iwa_fractalnoisefx.h @@ -113,7 +113,7 @@ protected: // ダイナミックの度合い TDoubleParamP m_dynamicIntensity; - //- - - Conical Noise - - - + //- - - Conical Noise - - - TBoolParamP m_doConical; TDoubleParamP m_conicalEvolution; TDoubleParamP m_conicalAngle; @@ -126,7 +126,7 @@ protected: public: Iwa_FractalNoiseFx(); bool canHandle(const TRenderSettings &info, double frame) override { - return true; + return !m_doConical->getValue(); } bool doGetBBox(double frame, TRectD &bBox, const TRenderSettings &ri) override; diff --git a/toonz/sources/tnztools/skeletontool.cpp b/toonz/sources/tnztools/skeletontool.cpp index b4c99dfe..efb0a0bf 100644 --- a/toonz/sources/tnztools/skeletontool.cpp +++ b/toonz/sources/tnztools/skeletontool.cpp @@ -413,21 +413,24 @@ void SkeletonTool::leftButtonDown(const TPointD &ppos, const TMouseEvent &e) { // lock/unlock: modalita IK if (TD_LockStageObject <= m_device && m_device < TD_LockStageObject + 1000) { - Skeleton* skeleton = new Skeleton(); - buildSkeleton(*skeleton, currentColumnIndex); int columnIndex = m_device - TD_LockStageObject; - int frame = app->getCurrentFrame()->getFrame(); - if (skeleton->getBoneByColumnIndex(columnIndex) == skeleton->getRootBone()) { - app->getCurrentColumn()->setColumnIndex(columnIndex); - m_device = TD_Translation; + int frame = app->getCurrentFrame()->getFrame(); + if (e.isCtrlPressed() || e.isShiftPressed()) { + // ctrl + click : toggle pinned center + // shift + click : toggle temporary pin + togglePinnedStatus(columnIndex, frame, e.isShiftPressed()); + invalidate(); + m_dragTool = 0; + return; } - else if (e.isShiftPressed()) { - togglePinnedStatus(columnIndex, frame, e.isShiftPressed()); - invalidate(); - m_dragTool = 0; - return; - } - else return; + Skeleton *skeleton = new Skeleton(); + buildSkeleton(*skeleton, currentColumnIndex); + if (skeleton->getBoneByColumnIndex(columnIndex) == + skeleton->getRootBone()) { + app->getCurrentColumn()->setColumnIndex(columnIndex); + m_device = TD_Translation; + } else + return; } switch (m_device) { diff --git a/toonz/sources/tnztools/tool.cpp b/toonz/sources/tnztools/tool.cpp index 27e9305d..1abe0116 100644 --- a/toonz/sources/tnztools/tool.cpp +++ b/toonz/sources/tnztools/tool.cpp @@ -34,6 +34,7 @@ #include "toonz/dpiscale.h" #include "toonz/palettecontroller.h" #include "toonz/tonionskinmaskhandle.h" +#include "toutputproperties.h" // TnzCore includes #include "tvectorimage.h" @@ -313,6 +314,11 @@ TImage *TTool::touchImage() { TFrameHandle *currentFrame = m_application->getCurrentFrame(); TXshLevelHandle *currentLevel = m_application->getCurrentLevel(); + TFrameId tmplFId = m_application->getCurrentScene() + ->getScene() + ->getProperties() + ->formatTemplateFIdForInput(); + if (currentFrame->isEditingLevel()) { // Editing level @@ -331,6 +337,8 @@ TImage *TTool::touchImage() { // create a new drawing img = sl->createEmptyFrame(); + // modify frameId to be with the same frame format as existing frames + sl->formatFId(fid, tmplFId); sl->setFrame(fid, img); currentLevel->notifyLevelChange(); m_isFrameCreated = true; @@ -414,6 +422,8 @@ TImage *TTool::touchImage() { // create the new drawing TImageP img = sl->createEmptyFrame(); m_isFrameCreated = true; + // modify frameId to be with the same frame format as existing frames + sl->formatFId(fid, tmplFId); // insert the drawing in the level sl->setFrame(fid, img); // update the cell @@ -507,6 +517,9 @@ TImage *TTool::touchImage() { // create the new drawing TImageP img = sl->createEmptyFrame(); m_isFrameCreated = true; + + // modify frameId to be with the same frame format as existing frames + sl->formatFId(fid, tmplFId); // insert the drawing in the level sl->setFrame(fid, img); // update the cell @@ -561,6 +574,8 @@ TImage *TTool::touchImage() { TFrameId fid = animationSheetEnabled ? getNewFrameId(sl, row) : TFrameId(1); TImageP img = sl->createEmptyFrame(); m_isFrameCreated = true; + // modify frameId to be with the same frame format as existing frames + sl->formatFId(fid, tmplFId); sl->setFrame(fid, img); cell = TXshCell(sl, fid); xsh->setCell(row, col, cell); diff --git a/toonz/sources/tnztools/typetool.cpp b/toonz/sources/tnztools/typetool.cpp index aac3e889..6b9c2bdd 100644 --- a/toonz/sources/tnztools/typetool.cpp +++ b/toonz/sources/tnztools/typetool.cpp @@ -1440,7 +1440,16 @@ void TypeTool::replaceText(std::wstring text, int from, int to) { for (unsigned int i = 0; i < (unsigned int)text.size(); i++) { wchar_t character = text[i]; - if (vi) { + // line break case. This can happen when pasting text including the line + // break + if (character == '\r') { + TVectorImageP vi(new TVectorImage); + unsigned int index = from + i; + m_string.insert(m_string.begin() + index, + StrokeChar(vi, -1., (int)(QChar('\r').unicode()), 0)); + } + + else if (vi) { TVectorImageP characterImage(new TVectorImage()); unsigned int index = from + i; // se il font ha kerning bisogna tenere conto del carattere successivo @@ -1466,7 +1475,7 @@ void TypeTool::replaceText(std::wstring text, int from, int to) { unsigned int index = from + i; if (instance->hasKerning() && (UINT)m_cursorIndex < m_string.size() && - !m_string[index].isReturn()) + index < m_string.size() - 1 && !m_string[index].isReturn()) adv = instance->drawChar((TRasterCM32P &)newRasterCM, p, styleId, (wchar_t)character, (wchar_t)m_string[index].m_key); diff --git a/toonz/sources/toonz/cellselection.cpp b/toonz/sources/toonz/cellselection.cpp index 172d02ca..d19db4ee 100644 --- a/toonz/sources/toonz/cellselection.cpp +++ b/toonz/sources/toonz/cellselection.cpp @@ -58,6 +58,8 @@ #include "vectorizerpopup.h" #include "tools/rasterselection.h" #include "tools/strokeselection.h" +#include "toonz/sceneproperties.h" +#include "toutputproperties.h" // TnzCore includes #include "timagecache.h" @@ -633,7 +635,9 @@ bool pasteRasterImageInCellWithoutUndo(int row, int col, TXshSimpleLevel *sl = 0; TFrameId fid(1); ToonzScene *scene = app->getCurrentScene()->getScene(); - TCamera *camera = scene->getCurrentCamera(); + TFrameId tmplFId = scene->getProperties()->formatTemplateFIdForInput(); + + TCamera *camera = scene->getCurrentCamera(); if (cell.isEmpty()) { if (row > 0) cell = xsh->getCell(row - 1, col); sl = cell.getSimpleLevel(); @@ -667,6 +671,10 @@ bool pasteRasterImageInCellWithoutUndo(int row, int col, img = sl->createEmptyFrame(); } else return false; + + // modify frameId to be with the same frame format as existing frames + sl->formatFId(fid, tmplFId); + sl->setFrame(fid, img); app->getCurrentLevel()->setLevel(sl); app->getCurrentLevel()->notifyLevelChange(); @@ -679,6 +687,10 @@ bool pasteRasterImageInCellWithoutUndo(int row, int col, std::vector fids; sl->getFids(fids); if (fids.size() > 0) fid = TFrameId(fids.back().getNumber() + 1); + + // modify frameId to be with the same frame format as existing frames + sl->formatFId(fid, tmplFId); + sl->setFrame(fid, img); } xsh->setCell(row, col, TXshCell(sl, fid)); @@ -3381,6 +3393,10 @@ static void createNewDrawing(TXsheet *xsh, int row, int col, while (fid.getLetter().toUtf8().at(0) < 'z' && sl->isFid(fid)) fid = TFrameId(fid.getNumber(), fid.getLetter().toUtf8().at(0) + 1); } + // modify frameId to be with the same frame format as existing frames + TFrameId tmplFId = + xsh->getScene()->getProperties()->formatTemplateFIdForInput(); + sl->formatFId(fid, tmplFId); // add the new frame sl->setFrame(fid, sl->createEmptyFrame()); TApp::instance()->getCurrentLevel()->notifyLevelChange(); diff --git a/toonz/sources/toonz/comboviewerpane.cpp b/toonz/sources/toonz/comboviewerpane.cpp index a3b5d824..bfaf0121 100644 --- a/toonz/sources/toonz/comboviewerpane.cpp +++ b/toonz/sources/toonz/comboviewerpane.cpp @@ -311,10 +311,9 @@ void ComboViewerPanel::onShowHideActionTriggered(QAction *act) { void ComboViewerPanel::onDrawFrame(int frame, const ImagePainter::VisualSettings &settings, - QElapsedTimer *timer, qint64 targetInstant) { + QElapsedTimer *, qint64) { TApp *app = TApp::instance(); m_sceneViewer->setVisual(settings); - m_sceneViewer->setTimerAndTargetInstant(timer, targetInstant); TFrameHandle *frameHandle = app->getCurrentFrame(); @@ -350,10 +349,6 @@ void ComboViewerPanel::onDrawFrame(int frame, else if (settings.m_blankColor != TPixel::Transparent) m_sceneViewer->update(); - - // make sure to redraw the frame here. - // repaint() does NOT immediately redraw the frame for QOpenGLWidget - if (frameHandle->isPlaying()) qApp->processEvents(); } //----------------------------------------------------------------------------- diff --git a/toonz/sources/toonz/convertpopup.cpp b/toonz/sources/toonz/convertpopup.cpp index 2ddddcbb..6c1fddaf 100644 --- a/toonz/sources/toonz/convertpopup.cpp +++ b/toonz/sources/toonz/convertpopup.cpp @@ -228,7 +228,8 @@ void ConvertPopup::Converter::convertLevel( TPixel32 bgColor = m_parent->m_bgColorField->getColor(); ImageUtils::convert(sourceFileFullPath, dstFileFullPath, from, to, framerate, prop, m_parent->m_notifier, bgColor, - m_parent->m_removeDotBeforeFrameNumber->isChecked()); + m_parent->m_removeDotBeforeFrameNumber->isChecked(), + oprop->formatTemplateFId()); } popup->m_notifier->notifyLevelCompleted(dstFileFullPath); @@ -1039,12 +1040,11 @@ TFilePath ConvertPopup::getDestinationFilePath( const TFilePath &sourceFilePath) { // Build the DECODED output folder path TFilePath destFolder = sourceFilePath.getParentDir(); + ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene(); if (!m_saveInFileFld->getPath().isEmpty()) { TFilePath dir(m_saveInFileFld->getPath().toStdWString()); - - ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene(); - destFolder = scene->decodeFilePath(dir); + destFolder = scene->decodeFilePath(dir); } // Build the output level name @@ -1059,10 +1059,11 @@ TFilePath ConvertPopup::getDestinationFilePath( TFilePath destName = TFilePath(name).withType(ext); if (TFileType::isLevelFilePath(sourceFilePath) && - !TFileType::isLevelExtension(ext)) - destName = destName.withFrame(TFrameId::EMPTY_FRAME); // use the '..' - // format to denote - // an output level + !TFileType::isLevelExtension(ext)) { + // add ".." or "_." according to the output settings' frame format template. + TOutputProperties *prop = scene->getProperties()->getOutputProperties(); + destName = destName.withFrame(prop->formatTemplateFId()); + } // Merge the two return destFolder + destName; @@ -1273,8 +1274,12 @@ void ConvertPopup::onOptionsClicked() { std::string ext = m_fileFormat->currentText().toStdString(); TPropertyGroup *props = getFormatProperties(ext); + // use output settings' frame format. + ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene(); + TOutputProperties *prop = scene->getProperties()->getOutputProperties(); + openFormatSettingsPopup( - this, ext, props, + this, ext, props, &prop->formatTemplateFId(), false, m_srcFilePaths.size() == 1 ? m_srcFilePaths[0] : TFilePath()); } diff --git a/toonz/sources/toonz/exportlevelcommand.cpp b/toonz/sources/toonz/exportlevelcommand.cpp index e0b7c90d..d2752e9c 100644 --- a/toonz/sources/toonz/exportlevelcommand.cpp +++ b/toonz/sources/toonz/exportlevelcommand.cpp @@ -442,8 +442,15 @@ bool IoCmd::exportLevel(const TFilePath &path, TXshSimpleLevel *sl, QString::fromStdString(m_sl->index2fid(i).expand()) + "." + QString::fromStdString(m_path.getType()); fpout = TFilePath(pathOut.toStdString()); - } else - fpout = TFilePath(m_path.withFrame(m_sl->index2fid(i))); + } else { + // change frame format for saving + TFrameId formattedFId(m_sl->index2fid(i)); + formattedFId.setZeroPadding( + m_opts.m_formatTemplateFId.getZeroPadding()); + formattedFId.setStartSeqInd( + m_opts.m_formatTemplateFId.getStartSeqInd()); + fpout = TFilePath(m_path.withFrame(formattedFId)); + } // Ask for overwrite permission in case a level with the built path // already exists diff --git a/toonz/sources/toonz/exportlevelcommand.h b/toonz/sources/toonz/exportlevelcommand.h index 72a54623..652e3d1d 100644 --- a/toonz/sources/toonz/exportlevelcommand.h +++ b/toonz/sources/toonz/exportlevelcommand.h @@ -46,9 +46,9 @@ struct ProgressCallbacks { virtual ~ProgressCallbacks() {} virtual void setProcessedName(const QString &name) = 0; - virtual void setRange(int min, int max) = 0; - virtual void setValue(int val) = 0; - virtual bool canceled() const = 0; + virtual void setRange(int min, int max) = 0; + virtual void setValue(int val) = 0; + virtual bool canceled() const = 0; }; //************************************************************************************ @@ -79,12 +79,15 @@ struct ExportLevelOptions { //! \li Transparent pixels mapped to white bool m_noAntialias; //!< Whether antialias must be removed from images. + TFrameId m_formatTemplateFId; + public: ExportLevelOptions() : m_props(0) , m_bgColor(TPixel32::Transparent) , m_forRetas(false) - , m_noAntialias(false) { + , m_noAntialias(false) + , m_formatTemplateFId() { m_thicknessTransform[0][0] = 0.0, m_thicknessTransform[0][1] = 1.0; m_thicknessTransform[1][0] = 0.0, m_thicknessTransform[1][1] = 1.0; } @@ -111,7 +114,7 @@ TImageP exportedImage( const TXshSimpleLevel &sl, //!< Level host of the image to be exported. const TFrameId &fid, //!< Frame of the image in sl. const ExportLevelOptions &opts = ExportLevelOptions() //!< Export options. - ); +); //--------------------------------------------------------------------- @@ -142,7 +145,7 @@ bool exportLevel( 0, //!< External callbacks to overwrite requests. ProgressCallbacks *progressCB = 0 //!< External callbacks to progress notifications. - ); +); } // namespace IoCmd diff --git a/toonz/sources/toonz/exportlevelpopup.cpp b/toonz/sources/toonz/exportlevelpopup.cpp index 05a286c5..8d3d5c33 100644 --- a/toonz/sources/toonz/exportlevelpopup.cpp +++ b/toonz/sources/toonz/exportlevelpopup.cpp @@ -36,6 +36,7 @@ #include "toonz/txshlevelhandle.h" #include "toonz/txsheethandle.h" #include "toonz/tscenehandle.h" +#include "toutputproperties.h" // TnzCore includes #include "tiio.h" @@ -477,6 +478,10 @@ IoCmd::ExportLevelOptions ExportLevelPopup::getOptions(const std::string &ext) { opts.m_props = getFormatProperties(ext); opts.m_forRetas = (m_retas->checkState() == Qt::Checked); + ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene(); + opts.m_formatTemplateFId = + scene->getProperties()->formatTemplateFIdForInput(); + return opts; } @@ -521,14 +526,19 @@ void ExportLevelPopup::checkAlpha() { void ExportLevelPopup::onOptionsClicked() { std::string ext = m_format->currentText().toStdString(); TPropertyGroup *props = getFormatProperties(ext); + // use preview settings' frame format. + // hide options when the Retas option is enabled. + TFrameId *tmplFId = nullptr; + if (!m_retas->isChecked()) { + ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene(); + tmplFId = &scene->getProperties()->formatTemplateFIdForInput(); + } + setModal(false); - if (DVGui::Dialog *dialog = - openFormatSettingsPopup(this, ext, props, m_browser->getFolder())) { - bool ret; - ret = connect(dialog, SIGNAL(dialogClosed()), SLOT(checkAlpha())), - assert(ret); - } + if (openFormatSettingsPopup(this, ext, props, tmplFId, true, + m_browser->getFolder())) + checkAlpha(); } //-------------------------------------------------------------- @@ -616,14 +626,19 @@ bool ExportLevelPopup::execute() { const std::string &ext = m_format->currentText().toStdString(); const IoCmd::ExportLevelOptions &opts = getOptions(ext); + TApp *app = TApp::instance(); + + ToonzScene *scene = app->getCurrentScene()->getScene(); + TFrameId tmplFId = scene->getProperties()->formatTemplateFIdForInput(); + // Retrieve current column selection - TApp *app = TApp::instance(); + TSelection *selection = app->getCurrentSelection()->getSelection(); TColumnSelection *colSelection = dynamic_cast(selection); if (colSelection && colSelection->getIndices().size() > 1) { fp = TFilePath(m_browser->getFolder() + TFilePath("a")) .withType(ext) - .withFrame(); + .withFrame(tmplFId); bool ret = true; MultiExportOverwriteCB overwriteCB; @@ -661,7 +676,7 @@ bool ExportLevelPopup::execute() { if (isReservedFileName_message(QString::fromStdString(fp.getName()))) return false; - return IoCmd::exportLevel(fp.withType(ext).withFrame(), 0, opts); + return IoCmd::exportLevel(fp.withType(ext).withFrame(tmplFId), 0, opts); } } diff --git a/toonz/sources/toonz/formatsettingspopups.cpp b/toonz/sources/toonz/formatsettingspopups.cpp index b3e366dd..36675397 100644 --- a/toonz/sources/toonz/formatsettingspopups.cpp +++ b/toonz/sources/toonz/formatsettingspopups.cpp @@ -21,6 +21,7 @@ #include "tlevel_io.h" #include "tproperty.h" #include "timageinfo.h" +#include "tfiletype.h" // Qt includes #include @@ -28,6 +29,17 @@ #include #include #include +#include + +namespace { +bool checkForSeqNum(QString type) { + TFileType::Type typeInfo = TFileType::getInfoFromExtension(type); + if ((typeInfo & TFileType::IMAGE) && !(typeInfo & TFileType::LEVEL)) + return true; + else + return false; +} +}; // namespace //********************************************************************************** // FormatSettingsPopup implementation @@ -35,7 +47,8 @@ FormatSettingsPopup::FormatSettingsPopup(QWidget *parent, const std::string &format, - TPropertyGroup *props) + TPropertyGroup *props, + TFrameId *tmplFrameId, bool forInput) : Dialog(parent) , m_format(format) , m_props(props) @@ -45,7 +58,9 @@ FormatSettingsPopup::FormatSettingsPopup(QWidget *parent, , m_codecComboBox(0) , m_configureCodec(0) #endif -{ + , m_sepCharCB(nullptr) + , m_paddingCB(nullptr) + , m_tmplFId(tmplFrameId) { setWindowTitle(tr("File Settings")); setWindowFlags(Qt::Dialog | Qt::WindowStaysOnTopHint); @@ -57,22 +72,24 @@ FormatSettingsPopup::FormatSettingsPopup(QWidget *parent, m_mainLayout->setColumnStretch(0, 0); m_mainLayout->setColumnStretch(1, 1); - int i = 0; - for (i = 0; i < m_props->getPropertyCount(); i++) { - if (dynamic_cast(m_props->getProperty(i))) - buildPropertyComboBox(i, m_props); - else if (dynamic_cast(m_props->getProperty(i))) - buildValueField(i, m_props); - else if (dynamic_cast(m_props->getProperty(i))) - buildDoubleField(i, m_props); - else if (dynamic_cast(m_props->getProperty(i))) { - if (m_props->getProperty(i)->getName() != - "Generate Palette") // Hide. Not using but still needed here + if (m_props) { + int i = 0; + for (i = 0; i < m_props->getPropertyCount(); i++) { + if (dynamic_cast(m_props->getProperty(i))) + buildPropertyComboBox(i, m_props); + else if (dynamic_cast(m_props->getProperty(i))) + buildValueField(i, m_props); + else if (dynamic_cast(m_props->getProperty(i))) + buildDoubleField(i, m_props); + else if (dynamic_cast(m_props->getProperty(i))) { + if (m_props->getProperty(i)->getName() != + "Generate Palette") // Hide. Not using but still needed here buildPropertyCheckBox(i, m_props); - } else if (dynamic_cast(m_props->getProperty(i))) - buildPropertyLineEdit(i, m_props); - else - assert(false); + } else if (dynamic_cast(m_props->getProperty(i))) + buildPropertyLineEdit(i, m_props); + else + assert(false); + } } #ifdef _WIN32 @@ -92,6 +109,51 @@ FormatSettingsPopup::FormatSettingsPopup(QWidget *parent, } #endif + if (checkForSeqNum(QString::fromStdString(format)) && tmplFrameId) { + m_sepCharCB = new QComboBox(this); + m_paddingCB = new QComboBox(this); + + m_sepCharCB->addItem(tr(". (period)"), QChar('.')); + m_sepCharCB->addItem(tr("_ (underscore)"), QChar('_')); + m_sepCharCB->setCurrentIndex(m_sepCharCB->findData( + QChar::fromLatin1(tmplFrameId->getStartSeqInd()))); + + m_paddingCB->addItem(tr("No padding"), 0); + for (int p = 1; p <= 6; p++) m_paddingCB->addItem(QString::number(p), p); + m_paddingCB->setCurrentIndex( + m_paddingCB->findData(tmplFrameId->getZeroPadding())); + + int currentRow = m_mainLayout->rowCount(); + if (currentRow > 0) + m_mainLayout->addItem(new QSpacerItem(15, 15), currentRow, 0); + + QGroupBox *frameFormatGB = new QGroupBox(tr("Frame Number Format"), this); + + frameFormatGB->setObjectName((forInput) ? "FrameFormatBoxInput" + : "FrameFormatBoxOutput"); + + QGridLayout *frameFormatLay = new QGridLayout(); + frameFormatLay->setColumnStretch(0, 0); + frameFormatLay->setColumnStretch(1, 1); + { + frameFormatLay->addWidget(new QLabel(tr("Separate Character:"), this), 0, + 0, Qt::AlignRight); + frameFormatLay->addWidget(m_sepCharCB, 0, 1, Qt::AlignLeft); + + frameFormatLay->addWidget(new QLabel(tr("Padding:"), this), 1, 0, + Qt::AlignRight); + frameFormatLay->addWidget(m_paddingCB, 1, 1, Qt::AlignLeft); + } + frameFormatGB->setLayout(frameFormatLay); + + m_mainLayout->addWidget(frameFormatGB, currentRow + 1, 0, 1, 2); + + connect(m_sepCharCB, SIGNAL(activated(int)), this, + SLOT(onSepCharCBChanged())); + connect(m_paddingCB, SIGNAL(activated(int)), this, + SLOT(onPaddingCBChanged())); + } + QPushButton *closeButton = new QPushButton(tr("Close")); closeButton->setContentsMargins(4, 4, 4, 4); closeButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); @@ -319,25 +381,40 @@ void FormatSettingsPopup::showEvent(QShowEvent *se) { Dialog::showEvent(se); } +//----------------------------------------------------------------------------- + +void FormatSettingsPopup::onSepCharCBChanged() { + assert(m_tmplFId); + m_tmplFId->setStartSeqInd(m_sepCharCB->currentData().toChar().toLatin1()); +} + +//----------------------------------------------------------------------------- + +void FormatSettingsPopup::onPaddingCBChanged() { + assert(m_tmplFId); + m_tmplFId->setZeroPadding(m_paddingCB->currentData().toInt()); +} + //********************************************************************************** // API functions //********************************************************************************** -FormatSettingsPopup *openFormatSettingsPopup(QWidget *parent, - const std::string &format, - TPropertyGroup *props, - const TFilePath &levelPath) { +bool openFormatSettingsPopup(QWidget *parent, const std::string &format, + TPropertyGroup *props, TFrameId *tmplFId, + bool forInput, const TFilePath &levelPath) { - if (!props || props->getPropertyCount() == 0) return 0; + if ((!props || props->getPropertyCount() == 0) && + !(checkForSeqNum(QString::fromStdString(format)) && tmplFId)) + return false; - FormatSettingsPopup *popup = new FormatSettingsPopup(parent, format, props); + FormatSettingsPopup *popup = + new FormatSettingsPopup(parent, format, props, tmplFId, forInput); popup->setAttribute(Qt::WA_DeleteOnClose); popup->setModal(true); if (!levelPath.isEmpty()) popup->setLevelPath(levelPath); - popup->show(); - popup->raise(); + popup->exec(); - return popup; + return true; } diff --git a/toonz/sources/toonz/formatsettingspopups.h b/toonz/sources/toonz/formatsettingspopups.h index 5e3bc27e..7ac6f08d 100644 --- a/toonz/sources/toonz/formatsettingspopups.h +++ b/toonz/sources/toonz/formatsettingspopups.h @@ -18,11 +18,12 @@ class TPropertyGroup; class QLabel; +class QComboBox; namespace DVGui { class PropertyWidget; class PropertyComboBox; -} +} // namespace DVGui //============================================================== @@ -35,7 +36,8 @@ class FormatSettingsPopup final : public DVGui::Dialog { public: FormatSettingsPopup(QWidget *parent, const std::string &format, - TPropertyGroup *props); + TPropertyGroup *props, TFrameId *tmplFrameId = nullptr, + bool forInput = true); void setLevelPath(const TFilePath &path) { m_levelPath = path; } void setFormatProperties(TPropertyGroup *props); @@ -59,6 +61,9 @@ private: #endif + TFrameId *m_tmplFId; + QComboBox *m_sepCharCB, *m_paddingCB; + private: void buildPropertyComboBox(int index, TPropertyGroup *props); void buildValueField(int index, TPropertyGroup *props); @@ -70,6 +75,9 @@ private: private Q_SLOTS: void onComboBoxIndexChanged(const QString &); void onAviCodecConfigure(); + + void onSepCharCBChanged(); + void onPaddingCBChanged(); }; //********************************************************************************** @@ -101,14 +109,18 @@ private Q_SLOTS: format. */ -FormatSettingsPopup *openFormatSettingsPopup( +bool openFormatSettingsPopup( QWidget *parent, //!< Parent for the new format popup. const std::string &format, //!< File extension of the displayed format. TPropertyGroup *props, //!< Properties to be shown for the format. + TFrameId *tmplFId = + nullptr, //!< Template TFrameId to specify frame number format + bool forInput = + true, // specifies border color for the frame number format box const TFilePath &levelPath = TFilePath() //!< May be used to choose available codecs //! depending on a level's resolution. - ); //!< Opens a suitable popup with settings +); //!< Opens a suitable popup with settings //! for an input level format. #endif // FORMATSETTINGSPOPUPS_H diff --git a/toonz/sources/toonz/levelcreatepopup.cpp b/toonz/sources/toonz/levelcreatepopup.cpp index 80b5a0fb..cf227d82 100644 --- a/toonz/sources/toonz/levelcreatepopup.cpp +++ b/toonz/sources/toonz/levelcreatepopup.cpp @@ -6,6 +6,7 @@ #include "menubarcommandids.h" #include "tapp.h" #include "levelcommand.h" +#include "formatsettingspopups.h" // TnzTools includes #include "tools/toolhandle.h" @@ -36,6 +37,7 @@ #include "toonz/tproject.h" #include "toonz/namebuilder.h" #include "toonz/childstack.h" +#include "toutputproperties.h" // TnzCore includes #include "tsystem.h" @@ -180,6 +182,10 @@ LevelCreatePopup::LevelCreatePopup() m_dpiLabel = new QLabel(tr("DPI:")); m_dpiFld = new DoubleLineEdit(0, 66.76); + m_rasterFormatLabel = new QLabel(tr("Format:")); + m_rasterFormatOm = new QComboBox(); + m_frameFormatBtn = new QPushButton(tr("Frame Format")); + QPushButton *okBtn = new QPushButton(tr("OK"), this); QPushButton *cancelBtn = new QPushButton(tr("Cancel"), this); QPushButton *applyBtn = new QPushButton(tr("Apply"), this); @@ -205,6 +211,11 @@ LevelCreatePopup::LevelCreatePopup() m_heightFld->setRange(0.1, (std::numeric_limits::max)()); m_dpiFld->setRange(0.1, (std::numeric_limits::max)()); + m_rasterFormatOm->addItem("tif", "tif"); + m_rasterFormatOm->addItem("png", "png"); + m_rasterFormatOm->setCurrentIndex(m_rasterFormatOm->findData( + Preferences::instance()->getDefRasterFormat())); + okBtn->setDefault(true); //--- layout @@ -247,15 +258,21 @@ LevelCreatePopup::LevelCreatePopup() Qt::AlignRight | Qt::AlignVCenter); guiLay->addWidget(m_pathFld, 4, 1, 1, 4); + // Format options (for Raster/Scan levels) + guiLay->addWidget(m_rasterFormatLabel, 5, 0, + Qt::AlignRight | Qt::AlignVCenter); + guiLay->addWidget(m_rasterFormatOm, 5, 1, Qt::AlignLeft); + guiLay->addWidget(m_frameFormatBtn, 5, 2, 1, 2, Qt::AlignLeft); + // Width - Height - guiLay->addWidget(m_widthLabel, 5, 0, Qt::AlignRight | Qt::AlignVCenter); - guiLay->addWidget(m_widthFld, 5, 1); - guiLay->addWidget(m_heightLabel, 5, 2, Qt::AlignRight | Qt::AlignVCenter); - guiLay->addWidget(m_heightFld, 5, 3); + guiLay->addWidget(m_widthLabel, 6, 0, Qt::AlignRight | Qt::AlignVCenter); + guiLay->addWidget(m_widthFld, 6, 1); + guiLay->addWidget(m_heightLabel, 6, 2, Qt::AlignRight | Qt::AlignVCenter); + guiLay->addWidget(m_heightFld, 6, 3); // DPI - guiLay->addWidget(m_dpiLabel, 6, 0, Qt::AlignRight | Qt::AlignVCenter); - guiLay->addWidget(m_dpiFld, 6, 1, 1, 3); + guiLay->addWidget(m_dpiLabel, 7, 0, Qt::AlignRight | Qt::AlignVCenter); + guiLay->addWidget(m_dpiFld, 7, 1); } guiLay->setColumnStretch(0, 0); guiLay->setColumnStretch(1, 0); @@ -280,12 +297,15 @@ LevelCreatePopup::LevelCreatePopup() bool ret = true; ret = ret && connect(m_levelTypeOm, SIGNAL(currentIndexChanged(int)), SLOT(onLevelTypeChanged(int))); + ret = ret && connect(m_frameFormatBtn, SIGNAL(clicked()), this, + SLOT(onFrameFormatButton())); ret = ret && connect(okBtn, SIGNAL(clicked()), this, SLOT(onOkBtn())); ret = ret && connect(cancelBtn, SIGNAL(clicked()), this, SLOT(reject())); ret = ret && connect(applyBtn, SIGNAL(clicked()), this, SLOT(onApplyButton())); setSizeWidgetEnable(false); + setRasterWidgetVisible(false); } //----------------------------------------------------------------------------- @@ -377,6 +397,15 @@ void LevelCreatePopup::setSizeWidgetEnable(bool isEnable) { //----------------------------------------------------------------------------- +void LevelCreatePopup::setRasterWidgetVisible(bool isVisible) { + m_rasterFormatLabel->setVisible(isVisible); + m_rasterFormatOm->setVisible(isVisible); + m_frameFormatBtn->setVisible(isVisible); + updateGeometry(); +} + +//----------------------------------------------------------------------------- + int LevelCreatePopup::getLevelType() const { return m_levelTypeOm->currentData().toInt(); } @@ -389,6 +418,9 @@ void LevelCreatePopup::onLevelTypeChanged(int index) { setSizeWidgetEnable(true); else setSizeWidgetEnable(false); + +// setRasterWidgetVisible(type == OVL_XSHLEVEL || type == TZI_XSHLEVEL); + updatePath(); std::wstring levelName = m_nameFld->text().toStdWString(); @@ -422,6 +454,17 @@ void LevelCreatePopup::onApplyButton() { //----------------------------------------------------------------------------- +void LevelCreatePopup::onFrameFormatButton() { + // Tentatively use the preview output settings + ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene(); + if (!scene) return; + std::string ext = m_rasterFormatOm->currentData().toString().toStdString(); + openFormatSettingsPopup(this, ext, nullptr, + &scene->getProperties()->formatTemplateFIdForInput()); +} + +//----------------------------------------------------------------------------- + bool LevelCreatePopup::apply() { TApp *app = TApp::instance(); int row = app->getCurrentFrame()->getFrame(); @@ -499,6 +542,10 @@ bool LevelCreatePopup::apply() { TFilePath fp = scene->getDefaultLevelPath(lType, levelName).withParentDir(parentDir); + if (lType == OVL_XSHLEVEL || lType == TZI_XSHLEVEL) + fp = fp.withType(Preferences::instance()->getDefRasterFormat().toStdString()); +// fp = fp.withType(m_rasterFormatOm->currentData().toString().toStdString()); + TFilePath actualFp = scene->decodeFilePath(fp); bool fileExists = TSystem::doesExistFileOrLevel(actualFp); if (!fileExists) { @@ -607,7 +654,6 @@ bool LevelCreatePopup::apply() { for (i = from; i <= to; i += inc) { TFrameId fid(i); - TXshCell cell(sl, fid); if (lType == PLI_XSHLEVEL) sl->setFrame(fid, new TVectorImage()); else if (lType == TZP_XSHLEVEL) { @@ -622,8 +668,12 @@ bool LevelCreatePopup::apply() { raster->clear(); TRasterImageP ri(raster); ri->setDpi(dpi, dpi); + // modify frameId to be with the same frame format as existing frames + TFrameId tmplFId = scene->getProperties()->formatTemplateFIdForInput(); + sl->formatFId(fid, tmplFId); sl->setFrame(fid, ri); } + TXshCell cell(sl, fid); for (j = 0; j < step; j++) xsh->setCell(row++, col, cell); } diff --git a/toonz/sources/toonz/levelcreatepopup.h b/toonz/sources/toonz/levelcreatepopup.h index 229137d5..b47f54bb 100644 --- a/toonz/sources/toonz/levelcreatepopup.h +++ b/toonz/sources/toonz/levelcreatepopup.h @@ -34,10 +34,16 @@ class LevelCreatePopup final : public DVGui::Dialog { DVGui::MeasuredDoubleLineEdit *m_heightFld; DVGui::DoubleLineEdit *m_dpiFld; + QLabel *m_rasterFormatLabel; + QComboBox *m_rasterFormatOm; + QPushButton *m_frameFormatBtn; + public: LevelCreatePopup(); void setSizeWidgetEnable(bool isEnable); + void setRasterWidgetVisible(bool isVisible); + int getLevelType() const; void update(); @@ -55,6 +61,7 @@ public slots: void onOkBtn(); void onApplyButton(); + void onFrameFormatButton(); }; #endif // LEVELCREATEPOPUP_H diff --git a/toonz/sources/toonz/outputsettingspopup.cpp b/toonz/sources/toonz/outputsettingspopup.cpp index 523cd645..df706942 100644 --- a/toonz/sources/toonz/outputsettingspopup.cpp +++ b/toonz/sources/toonz/outputsettingspopup.cpp @@ -38,6 +38,7 @@ #include "tenv.h" #include "tsystem.h" #include "tstream.h" +#include "tfiletype.h" // Qt includes #include @@ -145,6 +146,13 @@ enum ThreadsOption { enum GranularityOption { c_off, c_large, c_medium, c_small }; +bool checkForSeqNum(QString type) { + TFileType::Type typeInfo = TFileType::getInfoFromExtension(type); + if ((typeInfo & TFileType::IMAGE) && !(typeInfo & TFileType::LEVEL)) + return true; + else + return false; +} } // anonymous namespace //----------------------------------------------------------------------------- @@ -1238,10 +1246,10 @@ void OutputSettingsPopup::onNameChanged() { TOutputProperties *prop = getProperties(); TFilePath fp = prop->getPath(); - if (fp.getWideName() == wname) return; // Already had the right name + TFilePath newFp = fp.getParentDir() + TFilePath(wname).withType(fp.getType()); + if (newFp == fp) return; // Already had the right name - fp = fp.getParentDir() + TFilePath(wname).withType(fp.getType()); - prop->setPath(fp); + prop->setPath(newFp); TApp::instance()->getCurrentScene()->setDirtyFlag(true); @@ -1258,10 +1266,14 @@ void OutputSettingsPopup::onFormatChanged(const QString &str) { ext == "spritesheet"; }; - TOutputProperties *prop = getProperties(); + TOutputProperties *prop = getProperties(); bool wasMultiRenderInvalid = isMultiRenderInvalid(prop->getPath().getType(), m_allowMT); - TFilePath fp = prop->getPath().withType(str.toStdString()); + // remove sepchar, .. + TFilePath fp = prop->getPath().withNoFrame().withType(str.toStdString()); + // .. then add sepchar for sequencial image formats + if (checkForSeqNum(str)) fp = fp.withFrame(prop->formatTemplateFId()); + prop->setPath(fp); TApp::instance()->getCurrentScene()->setDirtyFlag(true); m_allowMT = Preferences::instance()->getFfmpegMultiThread(); @@ -1292,9 +1304,25 @@ void OutputSettingsPopup::onFormatChanged(const QString &str) { void OutputSettingsPopup::openSettingsPopup() { TOutputProperties *prop = getProperties(); std::string ext = prop->getPath().getType(); - openFormatSettingsPopup(this, ext, prop->getFileFormatProperties(ext)); + + TFrameId oldTmplFId = prop->formatTemplateFId(); + + bool ret = + openFormatSettingsPopup(this, ext, prop->getFileFormatProperties(ext), + &prop->formatTemplateFId(), false); + + if (!ret) return; if (m_presetCombo) m_presetCombo->setCurrentIndex(0); + + if (oldTmplFId.getZeroPadding() != + prop->formatTemplateFId().getZeroPadding() || + oldTmplFId.getStartSeqInd() != + prop->formatTemplateFId().getStartSeqInd()) { + TFilePath fp = + prop->getPath().withNoFrame().withFrame(prop->formatTemplateFId()); + prop->setPath(fp); + } } //----------------------------------------------------------------------------- diff --git a/toonz/sources/toonz/preferencespopup.cpp b/toonz/sources/toonz/preferencespopup.cpp index 8f4ebb9d..aaa41c9e 100644 --- a/toonz/sources/toonz/preferencespopup.cpp +++ b/toonz/sources/toonz/preferencespopup.cpp @@ -8,6 +8,7 @@ #include "levelsettingspopup.h" #include "tapp.h" #include "cleanupsettingsmodel.h" +#include "formatsettingspopups.h" #include "tenv.h" #include "mainwindow.h" @@ -29,6 +30,7 @@ #include "toonz/toonzscene.h" #include "toonz/tcamera.h" #include "toonz/levelproperties.h" +#include "toonz/sceneproperties.h" #include "toonz/tonionskinmaskhandle.h" #include "toonz/stage.h" #include "toonz/toonzfolders.h" @@ -785,6 +787,17 @@ void PreferencesPopup::onCheck30bitDisplay() { checker.exec(); } + +//----------------------------------------------------------------------------- + +void PreferencesPopup::onFrameFormatButton() { + ToonzScene* scene = TApp::instance()->getCurrentScene()->getScene(); + if (!scene) return; + std::string ext = Preferences::instance()->getDefRasterFormat().toStdString(); + openFormatSettingsPopup(this, ext, nullptr, + &scene->getProperties()->formatTemplateFIdForInput()); +} + //----------------------------------------------------------------------------- void PreferencesPopup::onAddLevelFormat() { @@ -1011,15 +1024,26 @@ void PreferencesPopup::insertUI(PreferencesItemId id, QGridLayout* layout, // CheckBox contains label in itself if (item.type == QMetaType::Bool) - layout->addWidget(widget, layout->rowCount(), 0, 1, 2); + layout->addWidget(widget, layout->rowCount(), 0, 1, 3, Qt::AlignLeft); else { // insert labels for other types int row = layout->rowCount(); layout->addWidget(new QLabel(getUIString(id), this), row, 0, Qt::AlignRight | Qt::AlignVCenter); if (isFileField) layout->addWidget(widget, row, 1, 1, 2); - else - layout->addWidget(widget, row, 1, Qt::AlignLeft | Qt::AlignVCenter); + else { + bool isWideComboBox = false; + for (auto cbItem : comboItems) { + if (widget->fontMetrics().width(cbItem.first) > 100) { + isWideComboBox = true; + break; + } + } + if (id == interfaceFont) isWideComboBox = true; + + layout->addWidget(widget, row, 1, 1, (isWideComboBox) ? 2 : 1, + Qt::AlignLeft | Qt::AlignVCenter); + } } } @@ -1054,7 +1078,7 @@ void PreferencesPopup::insertFootNote(QGridLayout* layout) { QLabel* note = new QLabel( tr("* Changes will take effect the next time you run Tahoma2D")); note->setStyleSheet("font-size: 10px; font: italic;"); - layout->addWidget(note, layout->rowCount(), 0, 1, 2, + layout->addWidget(note, layout->rowCount(), 0, 1, 3, Qt::AlignLeft | Qt::AlignVCenter); } @@ -1148,7 +1172,8 @@ QString PreferencesPopup::getUIString(PreferencesItemId id) { {rhubarbTimeout, tr("Analyze Audio Timeout (seconds):")}, // Drawing - {scanLevelType, tr("Scan File Format:")}, + {DefRasterFormat, tr("Default Raster Level Format:")}, + //{scanLevelType, tr("Scan File Format:")}, {DefLevelType, tr("Default Level Type:")}, {newLevelSizeToCameraSizeEnabled, tr("New Levels Default to the Current Camera Size")}, @@ -1229,7 +1254,7 @@ QString PreferencesPopup::getUIString(PreferencesItemId id) { {blanksCount, tr("Blank Frames:")}, {blankColor, tr("Blank Frames Color:")}, {rewindAfterPlayback, tr("Rewind after Playback")}, - {shortPlayFrameCount, tr("Number of Frames to Play for Short Play:")}, + {shortPlayFrameCount, tr("Number of Frames to Play \nfor Short Play:")}, {previewAlwaysOpenNewFlip, tr("Display in a New Flipbook Window")}, {fitToFlipbook, tr("Fit to Flipbook")}, {generatedMovieViewEnabled, tr("Open Flipbook after Rendering")}, @@ -1316,7 +1341,8 @@ QList PreferencesPopup::getComboItemList( {columnIconLoadingPolicy, {{tr("At Once"), Preferences::LoadAtOnce}, {tr("On Demand"), Preferences::LoadOnDemand}}}, - {scanLevelType, {{"tif", "tif"}, {"png", "png"}}}, + {DefRasterFormat, {{"tif", "tif"}, {"png", "png"}}}, + //{scanLevelType, {{"tif", "tif"}, {"png", "png"}}}, {DefLevelType, {{tr("Vector Level"), PLI_XSHLEVEL}, {tr("Smart Raster Level"), TZP_XSHLEVEL}, @@ -1574,7 +1600,7 @@ QWidget* PreferencesPopup::createInterfacePage() { // lay->addWidget(new QLabel(tr("Pixels Only:"), this), 5, 0, // Qt::AlignRight | Qt::AlignVCenter); - // lay->addWidget(createUI(pixelsOnly), 5, 1); + // lay->addWidget(createUI(pixelsOnly), 5, 1, 1, 2, Qt::AlignLeft); // insertUI(CurrentRoomChoice, lay, roomItemList); insertUI(functionEditorToggle, lay, getComboItemList(functionEditorToggle)); @@ -1593,7 +1619,7 @@ QWidget* PreferencesPopup::createInterfacePage() { #if QT_VERSION >= 0x051000 insertUI(displayIn30bit, lay); row = lay->rowCount(); - lay->addWidget(check30bitBtn, row - 1, 3); + lay->addWidget(check30bitBtn, row - 1, 2, Qt::AlignRight); #endif // insertUI(showIconsInMenu, lay); @@ -1760,7 +1786,7 @@ QWidget* PreferencesPopup::createImportExportPage() { insertUI(ffmpegTimeout, ffmpegOptionsLay); putLabel( - tr("Enabling multi-thread rendering will render significantly faster\n" + tr("Enabling multi-thread rendering will render significantly faster \n" "but a random crash might occur, use at your own risk:"), ffmpegOptionsLay); insertUI(ffmpegMultiThread, ffmpegOptionsLay); @@ -1783,10 +1809,15 @@ QWidget* PreferencesPopup::createImportExportPage() { QWidget* PreferencesPopup::createDrawingPage() { QWidget* widget = new QWidget(this); QGridLayout* lay = new QGridLayout(); + + QPushButton* frameFormatBtn = new QPushButton(tr("Default Frame Filename Format")); + setupLayout(lay); - // insertUI(scanLevelType, lay, getComboItemList(scanLevelType)); insertUI(DefLevelType, lay, getComboItemList(DefLevelType)); + insertUI(DefRasterFormat, lay, getComboItemList(DefRasterFormat)); + int row = lay->rowCount(); + lay->addWidget(frameFormatBtn, row - 1, 2, Qt::AlignLeft); insertUI(newLevelSizeToCameraSizeEnabled, lay); insertDualUIs(DefLevelWidth, DefLevelHeight, lay); // insertUI(DefLevelDpi, lay); @@ -1829,6 +1860,9 @@ QWidget* PreferencesPopup::createDrawingPage() { getUI(DefLevelHeight)->setDecimals(0); //} + bool ret = ret && connect(frameFormatBtn, SIGNAL(clicked()), this, + SLOT(onFrameFormatButton())); + return widget; } diff --git a/toonz/sources/toonz/preferencespopup.h b/toonz/sources/toonz/preferencespopup.h index 2ffbf725..fb0bcd00 100644 --- a/toonz/sources/toonz/preferencespopup.h +++ b/toonz/sources/toonz/preferencespopup.h @@ -179,6 +179,7 @@ private slots: void onInterfaceFontChanged(const QString& text); void onLutPathChanged(); void onCheck30bitDisplay(); + void onFrameFormatButton(); void onAddLevelFormat(); void onRemoveLevelFormat(); diff --git a/toonz/sources/toonz/sceneviewer.cpp b/toonz/sources/toonz/sceneviewer.cpp index 41cfa848..3abd98d4 100644 --- a/toonz/sources/toonz/sceneviewer.cpp +++ b/toonz/sources/toonz/sceneviewer.cpp @@ -800,8 +800,7 @@ SceneViewer::SceneViewer(ImageUtils::FullScreenWidget *parent) , m_mousePanning(0) , m_mouseZooming(0) , m_mouseRotating(0) - , m_keyAction(0) - , m_timer(nullptr) { + , m_keyAction(0) { m_visualSettings.m_sceneProperties = TApp::instance()->getCurrentScene()->getScene()->getProperties(); m_stopMotion = StopMotion::instance(); @@ -2053,14 +2052,6 @@ void SceneViewer::paintGL() { if (!m_isPicking && m_lutCalibrator && m_lutCalibrator->isValid()) m_lutCalibrator->onEndDraw(m_fbo); - - // wait to achieve precise fps - if (m_timer && m_timer->isValid()) { - qint64 currentInstant = m_timer->nsecsElapsed(); - while (currentInstant < m_targetInstant) { - currentInstant = m_timer->nsecsElapsed(); - } - } } //----------------------------------------------------------------------------- diff --git a/toonz/sources/toonz/sceneviewer.h b/toonz/sources/toonz/sceneviewer.h index 98a13558..d36710d0 100644 --- a/toonz/sources/toonz/sceneviewer.h +++ b/toonz/sources/toonz/sceneviewer.h @@ -219,10 +219,6 @@ class SceneViewer final : public GLWidgetForHighDpi, QAction *m_keyAction; - // passed from PlaybackExecutor - QElapsedTimer *m_timer; - qint64 m_targetInstant; - public: enum ReferenceMode { NORMAL_REFERENCE = 1, @@ -331,11 +327,6 @@ public: void setPreviewBGColor(const QColor &color) { m_previewBgColor = color; } QColor getPreviewBGColor() const { return m_previewBgColor; } - void setTimerAndTargetInstant(QElapsedTimer *timer, qint64 target) { - m_timer = timer; - m_targetInstant = target; - } - public: // SceneViewer's gadget public functions TPointD winToWorld(const QPointF &pos) const; diff --git a/toonz/sources/toonz/viewerpane.cpp b/toonz/sources/toonz/viewerpane.cpp index eff224b5..87cb9709 100644 --- a/toonz/sources/toonz/viewerpane.cpp +++ b/toonz/sources/toonz/viewerpane.cpp @@ -276,10 +276,9 @@ void SceneViewerPanel::onShowHideActionTriggered(QAction *act) { void SceneViewerPanel::onDrawFrame(int frame, const ImagePainter::VisualSettings &settings, - QElapsedTimer *timer, qint64 targetInstant) { + QElapsedTimer *, qint64) { TApp *app = TApp::instance(); m_sceneViewer->setVisual(settings); - m_sceneViewer->setTimerAndTargetInstant(timer, targetInstant); TFrameHandle *frameHandle = app->getCurrentFrame(); @@ -314,10 +313,6 @@ void SceneViewerPanel::onDrawFrame(int frame, else if (settings.m_blankColor != TPixel::Transparent) m_sceneViewer->update(); - - // make sure to redraw the frame here. - // repaint() does NOT immediately redraw the frame for QOpenGLWidget - if (frameHandle->isPlaying()) qApp->processEvents(); } //----------------------------------------------------------------------------- diff --git a/toonz/sources/toonz/xdtsio.cpp b/toonz/sources/toonz/xdtsio.cpp index b2fc7bcc..98cdb4c9 100644 --- a/toonz/sources/toonz/xdtsio.cpp +++ b/toonz/sources/toonz/xdtsio.cpp @@ -551,6 +551,8 @@ bool XdtsIo::loadXdtsScene(ToonzScene *scene, const TFilePath &scenePath) { int tick1Id, tick2Id; popup.getMarkerIds(tick1Id, tick2Id); + TFrameId tmplFId = scene->getProperties()->formatTemplateFIdForInput(); + TXsheet *xsh = scene->getXsheet(); XdtsTimeTableFieldItem cellField = xdtsData.timeTable().getCellField(); XdtsTimeTableHeaderItem cellHeader = xdtsData.timeTable().getCellHeader(); @@ -558,8 +560,9 @@ bool XdtsIo::loadXdtsScene(ToonzScene *scene, const TFilePath &scenePath) { QStringList layerNames = cellHeader.getLayerNames(); QList columns = cellField.getOccupiedColumns(); for (int column : columns) { - QString levelName = layerNames.at(column); - TXshLevel *level = levels.value(levelName); + QString levelName = layerNames.at(column); + TXshLevel *level = levels.value(levelName); + TXshSimpleLevel *sl = level->getSimpleLevel(); QList tick1, tick2; QVector track = cellField.getColumnTrack(column, tick1, tick2); @@ -568,13 +571,18 @@ bool XdtsIo::loadXdtsScene(ToonzScene *scene, const TFilePath &scenePath) { for (TFrameId fid : track) { if (fid.getNumber() == -1) // EMPTY cell case row++; - else + else { + // modify frameId to be with the same frame format as existing frames + if (sl) sl->formatFId(fid, tmplFId); xsh->setCell(row++, column, TXshCell(level, fid)); + } } // if the last cell is not "SYMBOL_NULL_CELL", continue the cell // to the end of the sheet TFrameId lastFid = track.last(); if (lastFid.getNumber() != -1) { + // modify frameId to be with the same frame format as existing frames + if (sl) sl->formatFId(lastFid, tmplFId); for (; row < duration; row++) xsh->setCell(row, column, TXshCell(level, TFrameId(lastFid))); } diff --git a/toonz/sources/toonz/xshcellmover.cpp b/toonz/sources/toonz/xshcellmover.cpp index a9fc40cc..120fae70 100644 --- a/toonz/sources/toonz/xshcellmover.cpp +++ b/toonz/sources/toonz/xshcellmover.cpp @@ -74,6 +74,9 @@ void CellsMover::start(int r0, int c0, int r1, int c1, int qualifiers, m_oldCells.resize(m_rowCount * m_colCount, TXshCell()); getCells(m_cells, r0, c0); getColumnsData(c0, c1); + + if (m_qualifiers & eInsertCells && !(m_qualifiers & eOverwriteCells)) + getImplicitCellInfo(); } // @@ -83,6 +86,46 @@ TXsheet *CellsMover::getXsheet() const { return TApp::instance()->getCurrentXsheet()->getXsheet(); } +// +// keeping the implicit cell arrangement when start shift + dragging +// + +void CellsMover::getImplicitCellInfo() { + int startCol = + (m_orientation->isVerticalTimeline()) ? m_startPos.x : m_startPos.y; + int startRow = + (m_orientation->isVerticalTimeline()) ? m_startPos.y : m_startPos.x; + + TXsheet *xsh = getXsheet(); + for (int i = 0; i < m_colCount; i++) { + TXshColumn *column = xsh->getColumn(startCol + i); + if (!column || column->getCellColumn() == 0 || column->isLocked()) continue; + TXshCellColumn *cellCol = column->getCellColumn(); + QMap cellInfo; + if (!column->isEmpty()) { + int r0, r1; + column->getRange(r0, r1); + TXshCell currentCell; + for (int r = r0; r <= r1 + 1; r++) { + TXshCell tmpCell = cellCol->getCell(r); + if (tmpCell != currentCell) { + if (m_qualifiers & eCopyCells) + cellInfo.insert(r, tmpCell); + else { + if (r < startRow) + cellInfo.insert(r, tmpCell); + else if (r >= startRow + m_rowCount) + cellInfo.insert(r - m_rowCount, tmpCell); + } + currentCell = tmpCell; + } + } + } + + m_implicitCellInfo.append(cellInfo); + } +} + // // cells <- xsheet // @@ -152,21 +195,25 @@ void CellsMover::moveCells(const TPoint &pos) const { int startCol = (m_orientation->isVerticalTimeline()) ? m_startPos.x : m_startPos.y; if (startCol == c) { + int infoId = 0; for (int i = 0; i < m_colCount; i++) { TXshColumn *column = xsh->getColumn(c + i); if (!column || column->getCellColumn() == 0 || column->isLocked()) continue; - // a cell above inserted cells - TXshCell upperCell = xsh->getCell(r - 1, c + i); - if (upperCell.isEmpty()) continue; + + QMap::const_iterator itr = + m_implicitCellInfo[infoId].lowerBound(r); + if (itr == m_implicitCellInfo[infoId].end() || itr.key() == r) { + infoId++; + continue; + } // a cell at the bottom of the inserted cells TXshCell bottomCell = xsh->getCell(r + m_rowCount - 1, c + i); - if (bottomCell.isEmpty()) continue; - int tmp_r = r + m_rowCount; - while (xsh->getCell(tmp_r, c + i) == upperCell) { + for (int tmp_r = r + m_rowCount; tmp_r <= itr.key() - 1 + m_rowCount; + tmp_r++) xsh->setCell(tmp_r, c + i, bottomCell); - tmp_r++; - } + + infoId++; } } } @@ -180,6 +227,7 @@ void CellsMover::undoMoveCells(const TPoint &pos) const { r = pos.x; c = pos.y; } + if (m_qualifiers & eInsertCells) { // Act like implicit hold when dragging cells with pressing Shift key // ( and WITHOUT Alt key ) and dragging within the same column. @@ -187,19 +235,27 @@ void CellsMover::undoMoveCells(const TPoint &pos) const { int startCol = (m_orientation->isVerticalTimeline()) ? m_startPos.x : m_startPos.y; if (startCol == c) { + int infoId = 0; for (int i = 0; i < m_colCount; i++) { - // a cell above selected cells - TXshCell upperCell = xsh->getCell(r - 1, c + i); - if (upperCell.isEmpty()) continue; - // a cell at the bottom of the selected cells - TXshCell bottomCell = xsh->getCell(r + m_rowCount - 1, c + i); - if (bottomCell.isEmpty()) continue; - int tmp_r = r + m_rowCount; - while (xsh->getCell(tmp_r, c + i) == bottomCell) { - xsh->setCell(tmp_r, c + i, upperCell); - tmp_r++; + TXshColumn *column = xsh->getColumn(c + i); + if (!column || column->getCellColumn() == 0 || column->isLocked()) + continue; + + // clear the cells and restore original arrangement + TXshCellColumn *cellCol = column->getCellColumn(); + int r0, r1; + cellCol->getRange(r0, r1); + cellCol->clearCells(r0, r1 - r0 + 1); + QList implicitKeys = m_implicitCellInfo[infoId].keys(); + for (int k = 0; k < implicitKeys.size() - 1; k++) { + int from = implicitKeys[k]; + int to = implicitKeys[k + 1] - 1; + for (int f = from; f <= to; f++) + cellCol->setCell(f, m_implicitCellInfo[infoId].value(from)); } + infoId++; } + return; } } @@ -589,7 +645,9 @@ void LevelMoverTool::onCellChange(int row, int col) { m_validPos = canMoveColumns(pos); if (!m_validPos) return; - if (m_moved) cellsMover->undoMoveCells(); + if (m_moved) { + cellsMover->undoMoveCells(); + } m_validPos = canMove(pos); if (m_validPos) { diff --git a/toonz/sources/toonz/xshcellmover.h b/toonz/sources/toonz/xshcellmover.h index 28d36bd9..2f987144 100644 --- a/toonz/sources/toonz/xshcellmover.h +++ b/toonz/sources/toonz/xshcellmover.h @@ -33,6 +33,9 @@ class CellsMover { const Orientation *m_orientation; + // keeping the implicit cell arrangement when start shift + dragging + QList> m_implicitCellInfo; + // helper method TXsheet *getXsheet() const; @@ -45,6 +48,8 @@ class CellsMover { // m_columnsData <- xsheet columns data void getColumnsData(int c0, int c1); + void getImplicitCellInfo(); + public: enum Qualifier { eCopyCells = 0x1, // leaves a copy of cells block at the starting point diff --git a/toonz/sources/toonz/xshcellviewer.cpp b/toonz/sources/toonz/xshcellviewer.cpp index b3766f10..2818c491 100644 --- a/toonz/sources/toonz/xshcellviewer.cpp +++ b/toonz/sources/toonz/xshcellviewer.cpp @@ -60,6 +60,7 @@ #include "toonz/doubleparamcmd.h" #include "toonz/preferences.h" #include "toonz/palettecontroller.h" +#include "toutputproperties.h" // TnzBase includes #include "tdoublekeyframe.h" @@ -892,6 +893,9 @@ void RenameCellField::renameCell() { TApp::instance()->getCurrentSelection()->getSelection()); if (!cellSelection) return; + ToonzScene *scene = m_viewer->getXsheet()->getScene(); + TFrameId tmplFId = scene->getProperties()->formatTemplateFIdForInput(); + QList cells; bool hasFrameZero = false; @@ -929,15 +933,20 @@ void RenameCellField::renameCell() { } } - TXshLevel *xl = cell.m_level.getPointer(); - if (!xl || (xl->getSimpleLevel() && !xl->getSimpleLevel()->isEmpty() && - xl->getSimpleLevel()->getFirstFid() == TFrameId::NO_FRAME)) { + TXshLevel *xl = cell.m_level.getPointer(); + TXshSimpleLevel *sl = (xl) ? xl->getSimpleLevel() : nullptr; + if (!xl || + (sl && !sl->isEmpty() && sl->getFirstFid() == TFrameId::NO_FRAME)) { cells.append(TXshCell()); continue; } // if the next upper cell is empty, then make this cell empty too if (fid == TFrameId::NO_FRAME) fid = (m_row - tmpRow <= 1) ? cell.m_frameId : TFrameId(0); + + // modify frameId to be with the same frame format as existing frames + if (sl) sl->formatFId(fid, tmplFId); + cells.append(TXshCell(xl, fid)); changed = true; hasFrameZero = (fid.getNumber() == 0 && xl->getSimpleLevel() && @@ -945,7 +954,6 @@ void RenameCellField::renameCell() { } if (!changed) return; } else { - ToonzScene *scene = m_viewer->getXsheet()->getScene(); TLevelSet *levelSet = scene->getLevelSet(); TXshLevel *xl = levelSet->getLevel(levelName); if (!xl && fid != TFrameId::NO_FRAME) { @@ -954,8 +962,11 @@ void RenameCellField::renameCell() { int levelType = pref->getDefLevelType(); xl = scene->createNewLevel(levelType, levelName); TXshSimpleLevel *sl = xl->getSimpleLevel(); - if (levelType == TZP_XSHLEVEL || levelType == OVL_XSHLEVEL) + if (levelType == TZP_XSHLEVEL || levelType == OVL_XSHLEVEL) { + // modify frameId to be with the same frame format as existing frames + sl->formatFId(fid, tmplFId); sl->setFrame(fid, sl->createEmptyFrame()); + } if (levelType == TZP_XSHLEVEL || levelType == PLI_XSHLEVEL) { TPalette *defaultPalette = TApp::instance()->getPaletteController()->getDefaultPalette( @@ -966,6 +977,10 @@ void RenameCellField::renameCell() { xl = scene->createNewLevel(TZI_XSHLEVEL, levelName); } if (!xl) return; + + // modify frameId to be with the same frame format as existing frames + if (xl->getSimpleLevel()) xl->getSimpleLevel()->formatFId(fid, tmplFId); + cells.append(TXshCell(xl, fid)); hasFrameZero = (fid.getNumber() == 0 && xl->getSimpleLevel() && xl->getSimpleLevel()->isFid(fid)); diff --git a/toonz/sources/toonzlib/levelupdater.cpp b/toonz/sources/toonzlib/levelupdater.cpp index 34c43ede..4a3e5c98 100644 --- a/toonz/sources/toonzlib/levelupdater.cpp +++ b/toonz/sources/toonzlib/levelupdater.cpp @@ -94,14 +94,15 @@ LevelUpdater::LevelUpdater(TXshSimpleLevel *sl) //----------------------------------------------------------------------------- -LevelUpdater::LevelUpdater(const TFilePath &fp, TPropertyGroup *lwProperties) +LevelUpdater::LevelUpdater(const TFilePath &fp, TPropertyGroup *lwProperties, + const TFrameId &tmplFId) : m_pg(0) , m_inputLevel(0) , m_imageInfo(0) , m_currIdx(0) , m_opened(false) , m_usingTemporaryFile(false) { - open(fp, lwProperties); + open(fp, lwProperties, tmplFId); } //----------------------------------------------------------------------------- @@ -203,7 +204,8 @@ void LevelUpdater::buildProperties(const TFilePath &fp) { //----------------------------------------------------------------------------- -void LevelUpdater::open(const TFilePath &fp, TPropertyGroup *pg) { +void LevelUpdater::open(const TFilePath &fp, TPropertyGroup *pg, + const TFrameId &tmplFId) { assert(!m_lw); // Find out if a corresponding level already exists on disk - in that case, @@ -257,6 +259,10 @@ void LevelUpdater::open(const TFilePath &fp, TPropertyGroup *pg) { assert(iconSize.lx > 0 && iconSize.ly > 0); m_lw->setIconSize(iconSize); + // set the frame format template (to be used in + // TLevelWriter::getFrameWriter()) + if (!tmplFId.isNoFrame()) m_lw->setFrameFormatTemplateFId(tmplFId); + m_opened = true; } diff --git a/toonz/sources/toonzlib/movierenderer.cpp b/toonz/sources/toonzlib/movierenderer.cpp index 03024296..8927a9a1 100644 --- a/toonz/sources/toonzlib/movierenderer.cpp +++ b/toonz/sources/toonzlib/movierenderer.cpp @@ -276,7 +276,8 @@ void MovieRenderer::Imp::prepareForStart() { locals::eraseUncompatibleExistingLevel(m_fp, cameraResI); m_levelUpdaterA.reset(new LevelUpdater( - m_fp, oprop->getFileFormatProperties(m_fp.getType()))); + m_fp, oprop->getFileFormatProperties(m_fp.getType()), + oprop->formatTemplateFId())); m_levelUpdaterA->getLevelWriter()->setFrameRate(frameRate); } else { TFilePath leftFp = m_fp.withName(m_fp.getName() + "_l"); @@ -286,11 +287,13 @@ void MovieRenderer::Imp::prepareForStart() { locals::eraseUncompatibleExistingLevel(rightFp, cameraResI); m_levelUpdaterA.reset(new LevelUpdater( - leftFp, oprop->getFileFormatProperties(leftFp.getType()))); + leftFp, oprop->getFileFormatProperties(leftFp.getType()), + oprop->formatTemplateFId())); m_levelUpdaterA->getLevelWriter()->setFrameRate(frameRate); m_levelUpdaterB.reset(new LevelUpdater( - rightFp, oprop->getFileFormatProperties(rightFp.getType()))); + rightFp, oprop->getFileFormatProperties(rightFp.getType()), + oprop->formatTemplateFId())); m_levelUpdaterB->getLevelWriter()->setFrameRate(frameRate); } } catch (...) { @@ -538,7 +541,7 @@ void MovieRenderer::Imp::doRenderRasterCompleted(const RenderData &renderData) { // This thread will be the one processing ft - remove it from the map to // prevent another // thread from interfering - double frame = ft->first; + double frame = ft->first; std::pair rasters = ft->second; ++m_nextFrameIdxToSave; diff --git a/toonz/sources/toonzlib/outputproperties.cpp b/toonz/sources/toonzlib/outputproperties.cpp index dc046144..a0a34730 100644 --- a/toonz/sources/toonzlib/outputproperties.cpp +++ b/toonz/sources/toonzlib/outputproperties.cpp @@ -41,7 +41,8 @@ TOutputProperties::TOutputProperties() , m_maxTileSizeIndex(0) , m_threadIndex(2) , m_subcameraPreview(false) - , m_boardSettings(new BoardSettings()) { + , m_boardSettings(new BoardSettings()) + , m_formatTemplateFId() { m_renderSettings = new TRenderSettings(); } @@ -61,7 +62,8 @@ TOutputProperties::TOutputProperties(const TOutputProperties &src) , m_maxTileSizeIndex(src.m_maxTileSizeIndex) , m_threadIndex(src.m_threadIndex) , m_subcameraPreview(src.m_subcameraPreview) - , m_boardSettings(new BoardSettings(*src.m_boardSettings)) { + , m_boardSettings(new BoardSettings(*src.m_boardSettings)) + , m_formatTemplateFId(src.m_formatTemplateFId) { std::map::iterator ft, fEnd = m_formatProperties.end(); for (ft = m_formatProperties.begin(); ft != fEnd; ++ft) { @@ -108,6 +110,8 @@ TOutputProperties &TOutputProperties::operator=(const TOutputProperties &src) { delete m_boardSettings; m_boardSettings = new BoardSettings(*src.m_boardSettings); + m_formatTemplateFId = src.m_formatTemplateFId; + return *this; } diff --git a/toonz/sources/toonzlib/preferences.cpp b/toonz/sources/toonzlib/preferences.cpp index 0c7d92aa..96af45a9 100644 --- a/toonz/sources/toonzlib/preferences.cpp +++ b/toonz/sources/toonzlib/preferences.cpp @@ -18,7 +18,6 @@ #include "tconvert.h" #include "tundo.h" #include "tbigmemorymanager.h" -#include "tfilepath.h" #include "timage_io.h" // Qt includes @@ -501,7 +500,8 @@ void Preferences::definePreferenceItems() { std::numeric_limits::max()); // Drawing - define(scanLevelType, "scanLevelType", QMetaType::QString, "tif"); + define(DefRasterFormat, "DefRasterFormat", QMetaType::QString, "png"); + // define(scanLevelType, "scanLevelType", QMetaType::QString, "tif"); define(DefLevelType, "DefLevelType", QMetaType::Int, OVL_XSHLEVEL); define(newLevelSizeToCameraSizeEnabled, "newLevelSizeToCameraSizeEnabled", QMetaType::Bool, true); @@ -788,6 +788,12 @@ void Preferences::resolveCompatibility() { else // Default (level name on top of each cell block) setValue(levelNameDisplayType, ShowLevelName_Default); } + // "scanLevelType" is changed to "DefRasterFormat", enabling to specify + // default format for both the Scan and the Raster levels. + if (m_settings->contains("scanLevelType") && + !m_settings->contains("DefRasterFormat")) { + setValue(DefRasterFormat, m_settings->value("scanLevelType").toString()); + } } //----------------------------------------------------------------- diff --git a/toonz/sources/toonzlib/sceneproperties.cpp b/toonz/sources/toonzlib/sceneproperties.cpp index 88b7495e..2534fb54 100644 --- a/toonz/sources/toonzlib/sceneproperties.cpp +++ b/toonz/sources/toonzlib/sceneproperties.cpp @@ -313,6 +313,16 @@ void TSceneProperties::saveData(TOStream &os) const { os.closeChild(); } + if (out.formatTemplateFId().getZeroPadding() != + TFrameId().getZeroPadding() || + out.formatTemplateFId().getStartSeqInd() != + TFrameId().getStartSeqInd()) { + os.openChild("frameFormat"); + os.child("padding") << (int)out.formatTemplateFId().getZeroPadding(); + os.child("sepchar") << QString(out.formatTemplateFId().getStartSeqInd()); + os.closeChild(); + } + os.closeChild(); // } os.closeChild(); @@ -680,6 +690,20 @@ void TSceneProperties::loadData(TIStream &is, bool isLoadingProject) { } else if (tagName == "clapperboardSettings") { assert(out.getBoardSettings()); out.getBoardSettings()->loadData(is); + } else if (tagName == "frameFormat") { + while (is.matchTag(tagName)) { + if (tagName == "padding") { + int padding; + is >> padding; + out.formatTemplateFId().setZeroPadding(padding); + } else if (tagName == "sepchar") { + QString sepChar; + is >> sepChar; + out.formatTemplateFId().setStartSeqInd(sepChar[0].toLatin1()); + } else + throw TException("unexpected tag: " + tagName); + is.closeChild(); + } // end while } else { throw TException("unexpected property tag: " + tagName); } @@ -835,4 +859,11 @@ bool TSceneProperties::hasDefaultCellMarks() const { return false; } return true; -} \ No newline at end of file +} + +//----------------------------------------------------------------------------- +// templateFId in preview settings is used for "input" file format +// such as new raster level, captured images by camera capture feature, etc. +TFrameId &TSceneProperties::formatTemplateFIdForInput() { + return m_previewProp->formatTemplateFId(); +} diff --git a/toonz/sources/toonzlib/sceneresources.cpp b/toonz/sources/toonzlib/sceneresources.cpp index de07a2c0..e79e19ae 100644 --- a/toonz/sources/toonzlib/sceneresources.cpp +++ b/toonz/sources/toonzlib/sceneresources.cpp @@ -407,7 +407,9 @@ void ScenePalette::rollbackPath() { m_pl->setPath(m_oldPath); } //----------------------------------------------------------------------------- -bool ScenePalette::isDirty() { return m_pl->getPalette()->getDirtyFlag(); } +bool ScenePalette::isDirty() { + return m_pl->getPalette() && m_pl->getPalette()->getDirtyFlag(); +} //----------------------------------------------------------------------------- diff --git a/toonz/sources/toonzlib/toonzscene.cpp b/toonz/sources/toonzlib/toonzscene.cpp index 880e4125..5d248d0c 100644 --- a/toonz/sources/toonzlib/toonzscene.cpp +++ b/toonz/sources/toonzlib/toonzscene.cpp @@ -1376,21 +1376,20 @@ TFilePath ToonzScene::getDefaultLevelPath(int levelType, TProject *project = getProject(); assert(project); TFilePath levelPath; - QString scanLevelType; switch (levelType) { case TZI_XSHLEVEL: - scanLevelType = Preferences::instance()->getScanLevelType(); - levelPath = TFilePath(levelName + L".." + scanLevelType.toStdWString()); - break; + case OVL_XSHLEVEL: { + QString rasterLevelType = Preferences::instance()->getDefRasterFormat(); + TFrameId tmplFId = getProperties()->formatTemplateFIdForInput(); + levelPath = TFilePath(levelName + L"." + rasterLevelType.toStdWString()) + .withFrame(tmplFId); + } break; case PLI_XSHLEVEL: levelPath = TFilePath(levelName).withType("pli"); break; case TZP_XSHLEVEL: levelPath = TFilePath(levelName).withType("tlv"); break; - case OVL_XSHLEVEL: - levelPath = TFilePath(levelName + L"..tif"); - break; default: levelPath = TFilePath(levelName + L"..png"); } diff --git a/toonz/sources/toonzlib/txshsimplelevel.cpp b/toonz/sources/toonzlib/txshsimplelevel.cpp index b4d5b067..c5f6d72c 100644 --- a/toonz/sources/toonzlib/txshsimplelevel.cpp +++ b/toonz/sources/toonzlib/txshsimplelevel.cpp @@ -34,6 +34,7 @@ #include "tstream.h" #include "tsystem.h" #include "tcontenthistory.h" +#include "tfilepath.h" // Qt includes #include @@ -781,6 +782,27 @@ TImageP buildIcon(const TImageP &img, const TDimension &size) { //----------------------------------------------------------------------------- +// modify frameId to be with the same frame format as existing frames +void TXshSimpleLevel::formatFId(TFrameId &fid, TFrameId _tmplFId) { + if (m_type != OVL_XSHLEVEL && m_type != TZI_XSHLEVEL) return; + + if (!m_frames.empty()) { + TFrameId tmplFId = *m_frames.begin(); + fid.setZeroPadding(tmplFId.getZeroPadding()); + fid.setStartSeqInd(tmplFId.getStartSeqInd()); + } + // since there is no reference frame, take sepChar from the path + else { + // override sepchar by the path + QChar sepChar = m_path.getSepChar(); + if (!sepChar.isNull()) _tmplFId.setStartSeqInd(sepChar.toLatin1()); + fid.setZeroPadding(_tmplFId.getZeroPadding()); + fid.setStartSeqInd(_tmplFId.getStartSeqInd()); + } +} + +//----------------------------------------------------------------------------- + void TXshSimpleLevel::setFrame(const TFrameId &fid, const TImageP &img) { assert(m_type != UNKNOWN_XSHLEVEL); diff --git a/toonz/sources/toonzqt/colorfield.cpp b/toonz/sources/toonzqt/colorfield.cpp index d23e5243..ddc5f276 100644 --- a/toonz/sources/toonzqt/colorfield.cpp +++ b/toonz/sources/toonzqt/colorfield.cpp @@ -490,7 +490,7 @@ ColorField::ColorField(QWidget *parent, bool isAlphaActive, TPixel32 color, */ void ColorField::setAlphaActive(bool active) { - if (active && !m_alphaChannel->isVisible()) { + if (active && !m_alphaChannel->isVisibleTo(this)) { m_alphaChannel->show(); connect(m_alphaChannel, SIGNAL(valueChanged(int, bool)), SLOT(onAlphaChannelChanged(int, bool))); @@ -499,7 +499,7 @@ void ColorField::setAlphaActive(bool active) { m_color.m = 0; m_colorSample->setColor(m_color); emit colorChanged(m_color, false); - } else if (!active && m_alphaChannel->isVisible()) { + } else if (!active && m_alphaChannel->isVisibleTo(this)) { m_alphaChannel->hide(); disconnect(m_alphaChannel, SIGNAL(valueChanged(int, bool)), this, SLOT(onAlphaChannelChanged(int, bool))); diff --git a/toonz/sources/toonzqt/flipconsole.cpp b/toonz/sources/toonzqt/flipconsole.cpp index 971d6dcd..e599fafc 100644 --- a/toonz/sources/toonzqt/flipconsole.cpp +++ b/toonz/sources/toonzqt/flipconsole.cpp @@ -220,10 +220,19 @@ void PlaybackExecutor::run() { shortTermDelayAdjuster -= delayAdjust; // Show the next frame, telling currently measured fps - // The wait time will be inserted at the end of paintGL in order to - // achieve precise playback + // For the Flipbook, the wait time will be inserted at the end of paintGL + // in order to achieve precise playback emit nextFrame(fps, &m_timer, targetInstant); + // Playing on Viewer / Combo Viewer will advance the current frame. + // Calling qApp->processEvents() on drawing frame causes repaint of other + // panels which slows playback. Therefore in Viewer / Combo Viewer panels + // it just calls update() and necessary wait will be inserted here. + qint64 currentInstant = m_timer.nsecsElapsed(); + while (currentInstant < targetInstant) { + currentInstant = m_timer.nsecsElapsed(); + } + if (FlipConsole::m_areLinked) { // In case there are linked consoles, update them too. // Their load time must be included in the fps calculation. diff --git a/toonz/sources/toonzqt/imageutils.cpp b/toonz/sources/toonzqt/imageutils.cpp index d7165e58..111c5623 100644 --- a/toonz/sources/toonzqt/imageutils.cpp +++ b/toonz/sources/toonzqt/imageutils.cpp @@ -592,7 +592,8 @@ static void convertFromVector(const TLevelReaderP &lr, const TLevelWriterP &lw, void convert(const TFilePath &source, const TFilePath &dest, const TFrameId &from, const TFrameId &to, double framerate, TPropertyGroup *prop, FrameTaskNotifier *frameNotifier, - const TPixel &bgColor, bool removeDotBeforeFrameNumber) { + const TPixel &bgColor, bool removeDotBeforeFrameNumber, + const TFrameId &tmplFId) { std::string dstExt = dest.getType(), srcExt = source.getType(); // Load source level structure @@ -621,6 +622,7 @@ void convert(const TFilePath &source, const TFilePath &dest, // Write the destination level TLevelWriterP lw(dest, prop); lw->setFrameRate(framerate); + lw->setFrameFormatTemplateFId(tmplFId); if (srcExt == "tlv") convertFromCM(lr, level->getPalette(), lw, frames, TAffine(), diff --git a/toonz/sources/toonzqt/styleeditor.cpp b/toonz/sources/toonzqt/styleeditor.cpp index b1bd29f3..0cd65ac5 100644 --- a/toonz/sources/toonzqt/styleeditor.cpp +++ b/toonz/sources/toonzqt/styleeditor.cpp @@ -4240,6 +4240,10 @@ QFrame *StyleEditor::createBottomWidget() { m_plainColorPage->m_rgbFrame->setVisible(false); menu->addSeparator(); + m_toggleOrientationAction = + new QAction(createQIcon("orientation_h"), tr("Toggle Orientation"), this); + menu->addAction(m_toggleOrientationAction); + m_hexEditorAction = new QAction(tr("Hex Color Names..."), this); menu->addAction(m_hexEditorAction); @@ -4258,14 +4262,6 @@ QFrame *StyleEditor::createBottomWidget() { // QToolBar *displayToolbar = new QToolBar(this); m_toolBar->addWidget(m_styleSetsButton); - m_toggleOrientationAction = - m_toolBar->addAction(createQIcon("orientation_h"), ""); - m_toggleOrientationAction->setToolTip( - tr("Toggle orientation of the Color Page.")); - QWidget *toggleOrientationButton = - m_toolBar->widgetForAction(m_toggleOrientationAction); - toggleOrientationButton->setFixedSize(22, 22); - toggleOrientationButton->setFocusPolicy(Qt::NoFocus); m_toolBar->addWidget(toolButton); m_toolBar->setMaximumHeight(22); m_toolBar->setIconSize(QSize(16, 16));